// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/compiler/bytecode-graph-builder.h"

#include "src/ast/ast.h"
#include "src/ast/scopes.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/compiler-source-position-table.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/simplified-operator.h"
#include "src/interpreter/bytecodes.h"
#include "src/objects-inl.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-generator.h"
#include "src/objects/literal-objects-inl.h"
#include "src/objects/smi.h"
#include "src/objects/template-objects-inl.h"
#include "src/vector-slot-pair.h"

namespace v8 {
namespace internal {
    namespace compiler {

        // The abstract execution environment simulates the content of the interpreter
        // register file. The environment performs SSA-renaming of all tracked nodes at
        // split and merge points in the control flow.
        class BytecodeGraphBuilder::Environment : public ZoneObject {
        public:
            Environment(BytecodeGraphBuilder* builder, int register_count,
                int parameter_count,
                interpreter::Register incoming_new_target_or_generator,
                Node* control_dependency);

            // Specifies whether environment binding methods should attach frame state
            // inputs to nodes representing the value being bound. This is done because
            // the {OutputFrameStateCombine} is closely related to the binding method.
            enum FrameStateAttachmentMode { kAttachFrameState,
                kDontAttachFrameState };

            int parameter_count() const { return parameter_count_; }
            int register_count() const { return register_count_; }

            Node* LookupAccumulator() const;
            Node* LookupRegister(interpreter::Register the_register) const;
            Node* LookupGeneratorState() const;

            void BindAccumulator(Node* node,
                FrameStateAttachmentMode mode = kDontAttachFrameState);
            void BindRegister(interpreter::Register the_register, Node* node,
                FrameStateAttachmentMode mode = kDontAttachFrameState);
            void BindRegistersToProjections(
                interpreter::Register first_reg, Node* node,
                FrameStateAttachmentMode mode = kDontAttachFrameState);
            void BindGeneratorState(Node* node);
            void RecordAfterState(Node* node,
                FrameStateAttachmentMode mode = kDontAttachFrameState);

            // Effect dependency tracked by this environment.
            Node* GetEffectDependency() { return effect_dependency_; }
            void UpdateEffectDependency(Node* dependency)
            {
                effect_dependency_ = dependency;
            }

            // Preserve a checkpoint of the environment for the IR graph. Any
            // further mutation of the environment will not affect checkpoints.
            Node* Checkpoint(BailoutId bytecode_offset, OutputFrameStateCombine combine,
                const BytecodeLivenessState* liveness);

            // Control dependency tracked by this environment.
            Node* GetControlDependency() const { return control_dependency_; }
            void UpdateControlDependency(Node* dependency)
            {
                control_dependency_ = dependency;
            }

            Node* Context() const { return context_; }
            void SetContext(Node* new_context) { context_ = new_context; }

            Environment* Copy();
            void Merge(Environment* other, const BytecodeLivenessState* liveness);

            void FillWithOsrValues();
            void PrepareForLoop(const BytecodeLoopAssignments& assignments,
                const BytecodeLivenessState* liveness);
            void PrepareForLoopExit(Node* loop,
                const BytecodeLoopAssignments& assignments,
                const BytecodeLivenessState* liveness);

        private:
            explicit Environment(const Environment* copy);

            bool StateValuesRequireUpdate(Node** state_values, Node** values, int count);
            void UpdateStateValues(Node** state_values, Node** values, int count);
            Node* GetStateValuesFromCache(Node** values, int count,
                const BitVector* liveness, int liveness_offset);

            int RegisterToValuesIndex(interpreter::Register the_register) const;

            Zone* zone() const { return builder_->local_zone(); }
            Graph* graph() const { return builder_->graph(); }
            CommonOperatorBuilder* common() const { return builder_->common(); }
            BytecodeGraphBuilder* builder() const { return builder_; }
            const NodeVector* values() const { return &values_; }
            NodeVector* values() { return &values_; }
            int register_base() const { return register_base_; }
            int accumulator_base() const { return accumulator_base_; }

            BytecodeGraphBuilder* builder_;
            int register_count_;
            int parameter_count_;
            Node* context_;
            Node* control_dependency_;
            Node* effect_dependency_;
            NodeVector values_;
            Node* parameters_state_values_;
            Node* generator_state_;
            int register_base_;
            int accumulator_base_;
        };

        // A helper for creating a temporary sub-environment for simple branches.
        struct BytecodeGraphBuilder::SubEnvironment final {
        public:
            explicit SubEnvironment(BytecodeGraphBuilder* builder)
                : builder_(builder)
                , parent_(builder->environment()->Copy())
            {
            }

            ~SubEnvironment() { builder_->set_environment(parent_); }

        private:
            BytecodeGraphBuilder* builder_;
            BytecodeGraphBuilder::Environment* parent_;
        };

        // Issues:
        // - Scopes - intimately tied to AST. Need to eval what is needed.
        // - Need to resolve closure parameter treatment.
        BytecodeGraphBuilder::Environment::Environment(
            BytecodeGraphBuilder* builder, int register_count, int parameter_count,
            interpreter::Register incoming_new_target_or_generator,
            Node* control_dependency)
            : builder_(builder)
            , register_count_(register_count)
            , parameter_count_(parameter_count)
            , control_dependency_(control_dependency)
            , effect_dependency_(control_dependency)
            , values_(builder->local_zone())
            , parameters_state_values_(nullptr)
            , generator_state_(nullptr)
        {
            // The layout of values_ is:
            //
            // [receiver] [parameters] [registers] [accumulator]
            //
            // parameter[0] is the receiver (this), parameters 1..N are the
            // parameters supplied to the method (arg0..argN-1). The accumulator
            // is stored separately.

            // Parameters including the receiver
            for (int i = 0; i < parameter_count; i++) {
                const char* debug_name = (i == 0) ? "%this" : nullptr;
                const Operator* op = common()->Parameter(i, debug_name);
                Node* parameter = builder->graph()->NewNode(op, graph()->start());
                values()->push_back(parameter);
            }

            // Registers
            register_base_ = static_cast<int>(values()->size());
            Node* undefined_constant = builder->jsgraph()->UndefinedConstant();
            values()->insert(values()->end(), register_count, undefined_constant);

            // Accumulator
            accumulator_base_ = static_cast<int>(values()->size());
            values()->push_back(undefined_constant);

            // Context
            int context_index = Linkage::GetJSCallContextParamIndex(parameter_count);
            const Operator* op = common()->Parameter(context_index, "%context");
            context_ = builder->graph()->NewNode(op, graph()->start());

            // Incoming new.target or generator register
            if (incoming_new_target_or_generator.is_valid()) {
                int new_target_index = Linkage::GetJSCallNewTargetParamIndex(parameter_count);
                const Operator* op = common()->Parameter(new_target_index, "%new.target");
                Node* new_target_node = builder->graph()->NewNode(op, graph()->start());

                int values_index = RegisterToValuesIndex(incoming_new_target_or_generator);
                values()->at(values_index) = new_target_node;
            }
        }

        BytecodeGraphBuilder::Environment::Environment(
            const BytecodeGraphBuilder::Environment* other)
            : builder_(other->builder_)
            , register_count_(other->register_count_)
            , parameter_count_(other->parameter_count_)
            , context_(other->context_)
            , control_dependency_(other->control_dependency_)
            , effect_dependency_(other->effect_dependency_)
            , values_(other->zone())
            , parameters_state_values_(other->parameters_state_values_)
            , generator_state_(other->generator_state_)
            , register_base_(other->register_base_)
            , accumulator_base_(other->accumulator_base_)
        {
            values_ = other->values_;
        }

        int BytecodeGraphBuilder::Environment::RegisterToValuesIndex(
            interpreter::Register the_register) const
        {
            if (the_register.is_parameter()) {
                return the_register.ToParameterIndex(parameter_count());
            } else {
                return the_register.index() + register_base();
            }
        }

        Node* BytecodeGraphBuilder::Environment::LookupAccumulator() const
        {
            return values()->at(accumulator_base_);
        }

        Node* BytecodeGraphBuilder::Environment::LookupGeneratorState() const
        {
            DCHECK_NOT_NULL(generator_state_);
            return generator_state_;
        }

        Node* BytecodeGraphBuilder::Environment::LookupRegister(
            interpreter::Register the_register) const
        {
            if (the_register.is_current_context()) {
                return Context();
            } else if (the_register.is_function_closure()) {
                return builder()->GetFunctionClosure();
            } else {
                int values_index = RegisterToValuesIndex(the_register);
                return values()->at(values_index);
            }
        }

        void BytecodeGraphBuilder::Environment::BindAccumulator(
            Node* node, FrameStateAttachmentMode mode)
        {
            if (mode == FrameStateAttachmentMode::kAttachFrameState) {
                builder()->PrepareFrameState(node, OutputFrameStateCombine::PokeAt(0));
            }
            values()->at(accumulator_base_) = node;
        }

        void BytecodeGraphBuilder::Environment::BindGeneratorState(Node* node)
        {
            generator_state_ = node;
        }

        void BytecodeGraphBuilder::Environment::BindRegister(
            interpreter::Register the_register, Node* node,
            FrameStateAttachmentMode mode)
        {
            int values_index = RegisterToValuesIndex(the_register);
            if (mode == FrameStateAttachmentMode::kAttachFrameState) {
                builder()->PrepareFrameState(node, OutputFrameStateCombine::PokeAt(accumulator_base_ - values_index));
            }
            values()->at(values_index) = node;
        }

        void BytecodeGraphBuilder::Environment::BindRegistersToProjections(
            interpreter::Register first_reg, Node* node,
            FrameStateAttachmentMode mode)
        {
            int values_index = RegisterToValuesIndex(first_reg);
            if (mode == FrameStateAttachmentMode::kAttachFrameState) {
                builder()->PrepareFrameState(node, OutputFrameStateCombine::PokeAt(accumulator_base_ - values_index));
            }
            for (int i = 0; i < node->op()->ValueOutputCount(); i++) {
                values()->at(values_index + i) = builder()->NewNode(common()->Projection(i), node);
            }
        }

        void BytecodeGraphBuilder::Environment::RecordAfterState(
            Node* node, FrameStateAttachmentMode mode)
        {
            if (mode == FrameStateAttachmentMode::kAttachFrameState) {
                builder()->PrepareFrameState(node, OutputFrameStateCombine::Ignore());
            }
        }

        BytecodeGraphBuilder::Environment* BytecodeGraphBuilder::Environment::Copy()
        {
            return new (zone()) Environment(this);
        }

        void BytecodeGraphBuilder::Environment::Merge(
            BytecodeGraphBuilder::Environment* other,
            const BytecodeLivenessState* liveness)
        {
            // Create a merge of the control dependencies of both environments and update
            // the current environment's control dependency accordingly.
            Node* control = builder()->MergeControl(GetControlDependency(),
                other->GetControlDependency());
            UpdateControlDependency(control);

            // Create a merge of the effect dependencies of both environments and update
            // the current environment's effect dependency accordingly.
            Node* effect = builder()->MergeEffect(GetEffectDependency(),
                other->GetEffectDependency(), control);
            UpdateEffectDependency(effect);

            // Introduce Phi nodes for values that are live and have differing inputs at
            // the merge point, potentially extending an existing Phi node if possible.
            context_ = builder()->MergeValue(context_, other->context_, control);
            for (int i = 0; i < parameter_count(); i++) {
                values_[i] = builder()->MergeValue(values_[i], other->values_[i], control);
            }
            for (int i = 0; i < register_count(); i++) {
                int index = register_base() + i;
                if (liveness == nullptr || liveness->RegisterIsLive(i)) {
#if DEBUG
                    // We only do these DCHECKs when we are not in the resume path of a
                    // generator -- this is, when either there is no generator state at all,
                    // or the generator state is not the constant "executing" value.
                    if (generator_state_ == nullptr || NumberMatcher(generator_state_).Is(JSGeneratorObject::kGeneratorExecuting)) {
                        DCHECK_NE(values_[index], builder()->jsgraph()->OptimizedOutConstant());
                        DCHECK_NE(other->values_[index],
                            builder()->jsgraph()->OptimizedOutConstant());
                    }
#endif

                    values_[index] = builder()->MergeValue(values_[index], other->values_[index], control);

                } else {
                    values_[index] = builder()->jsgraph()->OptimizedOutConstant();
                }
            }

            if (liveness == nullptr || liveness->AccumulatorIsLive()) {
                DCHECK_NE(values_[accumulator_base()],
                    builder()->jsgraph()->OptimizedOutConstant());
                DCHECK_NE(other->values_[accumulator_base()],
                    builder()->jsgraph()->OptimizedOutConstant());

                values_[accumulator_base()] = builder()->MergeValue(values_[accumulator_base()],
                    other->values_[accumulator_base()], control);
            } else {
                values_[accumulator_base()] = builder()->jsgraph()->OptimizedOutConstant();
            }

            if (generator_state_ != nullptr) {
                DCHECK_NOT_NULL(other->generator_state_);
                generator_state_ = builder()->MergeValue(generator_state_,
                    other->generator_state_, control);
            }
        }

        void BytecodeGraphBuilder::Environment::PrepareForLoop(
            const BytecodeLoopAssignments& assignments,
            const BytecodeLivenessState* liveness)
        {
            // Create a control node for the loop header.
            Node* control = builder()->NewLoop();

            // Create a Phi for external effects.
            Node* effect = builder()->NewEffectPhi(1, GetEffectDependency(), control);
            UpdateEffectDependency(effect);

            // Create Phis for any values that are live on entry to the loop and may be
            // updated by the end of the loop.
            context_ = builder()->NewPhi(1, context_, control);
            for (int i = 0; i < parameter_count(); i++) {
                if (assignments.ContainsParameter(i)) {
                    values_[i] = builder()->NewPhi(1, values_[i], control);
                }
            }
            for (int i = 0; i < register_count(); i++) {
                if (assignments.ContainsLocal(i) && (liveness == nullptr || liveness->RegisterIsLive(i))) {
                    int index = register_base() + i;
                    values_[index] = builder()->NewPhi(1, values_[index], control);
                }
            }
            // The accumulator should not be live on entry.
            DCHECK_IMPLIES(liveness != nullptr, !liveness->AccumulatorIsLive());

            if (generator_state_ != nullptr) {
                generator_state_ = builder()->NewPhi(1, generator_state_, control);
            }

            // Connect to the loop end.
            Node* terminate = builder()->graph()->NewNode(
                builder()->common()->Terminate(), effect, control);
            builder()->exit_controls_.push_back(terminate);
        }

        void BytecodeGraphBuilder::Environment::FillWithOsrValues()
        {
            Node* start = graph()->start();

            // Create OSR values for each environment value.
            SetContext(graph()->NewNode(
                common()->OsrValue(Linkage::kOsrContextSpillSlotIndex), start));
            int size = static_cast<int>(values()->size());
            for (int i = 0; i < size; i++) {
                int idx = i; // Indexing scheme follows {StandardFrame}, adapt accordingly.
                if (i >= register_base())
                    idx += InterpreterFrameConstants::kExtraSlotCount;
                if (i >= accumulator_base())
                    idx = Linkage::kOsrAccumulatorRegisterIndex;
                values()->at(i) = graph()->NewNode(common()->OsrValue(idx), start);
            }
        }

        bool BytecodeGraphBuilder::Environment::StateValuesRequireUpdate(
            Node** state_values, Node** values, int count)
        {
            if (*state_values == nullptr) {
                return true;
            }
            Node::Inputs inputs = (*state_values)->inputs();
            if (inputs.count() != count)
                return true;
            for (int i = 0; i < count; i++) {
                if (inputs[i] != values[i]) {
                    return true;
                }
            }
            return false;
        }

        void BytecodeGraphBuilder::Environment::PrepareForLoopExit(
            Node* loop, const BytecodeLoopAssignments& assignments,
            const BytecodeLivenessState* liveness)
        {
            DCHECK_EQ(loop->opcode(), IrOpcode::kLoop);

            Node* control = GetControlDependency();

            // Create the loop exit node.
            Node* loop_exit = graph()->NewNode(common()->LoopExit(), control, loop);
            UpdateControlDependency(loop_exit);

            // Rename the effect.
            Node* effect_rename = graph()->NewNode(common()->LoopExitEffect(),
                GetEffectDependency(), loop_exit);
            UpdateEffectDependency(effect_rename);

            // TODO(jarin) We should also rename context here. However, unconditional
            // renaming confuses global object and native context specialization.
            // We should only rename if the context is assigned in the loop.

            // Rename the environment values if they were assigned in the loop and are
            // live after exiting the loop.
            for (int i = 0; i < parameter_count(); i++) {
                if (assignments.ContainsParameter(i)) {
                    Node* rename = graph()->NewNode(common()->LoopExitValue(), values_[i], loop_exit);
                    values_[i] = rename;
                }
            }
            for (int i = 0; i < register_count(); i++) {
                if (assignments.ContainsLocal(i) && (liveness == nullptr || liveness->RegisterIsLive(i))) {
                    Node* rename = graph()->NewNode(common()->LoopExitValue(),
                        values_[register_base() + i], loop_exit);
                    values_[register_base() + i] = rename;
                }
            }
            if (liveness == nullptr || liveness->AccumulatorIsLive()) {
                Node* rename = graph()->NewNode(common()->LoopExitValue(),
                    values_[accumulator_base()], loop_exit);
                values_[accumulator_base()] = rename;
            }

            if (generator_state_ != nullptr) {
                generator_state_ = graph()->NewNode(common()->LoopExitValue(),
                    generator_state_, loop_exit);
            }
        }

        void BytecodeGraphBuilder::Environment::UpdateStateValues(Node** state_values,
            Node** values,
            int count)
        {
            if (StateValuesRequireUpdate(state_values, values, count)) {
                const Operator* op = common()->StateValues(count, SparseInputMask::Dense());
                (*state_values) = graph()->NewNode(op, count, values);
            }
        }

        Node* BytecodeGraphBuilder::Environment::GetStateValuesFromCache(
            Node** values, int count, const BitVector* liveness, int liveness_offset)
        {
            return builder_->state_values_cache_.GetNodeForValues(
                values, static_cast<size_t>(count), liveness, liveness_offset);
        }

        Node* BytecodeGraphBuilder::Environment::Checkpoint(
            BailoutId bailout_id, OutputFrameStateCombine combine,
            const BytecodeLivenessState* liveness)
        {
            if (parameter_count() == register_count()) {
                // Re-use the state-value cache if the number of local registers happens
                // to match the parameter count.
                parameters_state_values_ = GetStateValuesFromCache(
                    &values()->at(0), parameter_count(), nullptr, 0);
            } else {
                UpdateStateValues(&parameters_state_values_, &values()->at(0),
                    parameter_count());
            }

            Node* registers_state_values = GetStateValuesFromCache(&values()->at(register_base()), register_count(),
                liveness ? &liveness->bit_vector() : nullptr, 0);

            bool accumulator_is_live = !liveness || liveness->AccumulatorIsLive();
            Node* accumulator_state_value = accumulator_is_live && combine != OutputFrameStateCombine::PokeAt(0)
                ? values()->at(accumulator_base())
                : builder()->jsgraph()->OptimizedOutConstant();

            const Operator* op = common()->FrameState(
                bailout_id, combine, builder()->frame_state_function_info());
            Node* result = graph()->NewNode(
                op, parameters_state_values_, registers_state_values,
                accumulator_state_value, Context(), builder()->GetFunctionClosure(),
                builder()->graph()->start());

            return result;
        }

        BytecodeGraphBuilder::BytecodeGraphBuilder(
            Zone* local_zone, Handle<BytecodeArray> bytecode_array,
            Handle<SharedFunctionInfo> shared_info,
            Handle<FeedbackVector> feedback_vector, BailoutId osr_offset,
            JSGraph* jsgraph, CallFrequency& invocation_frequency,
            SourcePositionTable* source_positions, Handle<Context> native_context,
            int inlining_id, JSTypeHintLowering::Flags flags, bool stack_check,
            bool analyze_environment_liveness)
            : local_zone_(local_zone)
            , jsgraph_(jsgraph)
            , invocation_frequency_(invocation_frequency)
            , bytecode_array_(bytecode_array)
            , feedback_vector_(feedback_vector)
            , type_hint_lowering_(jsgraph, feedback_vector, flags)
            , frame_state_function_info_(common()->CreateFrameStateFunctionInfo(
                  FrameStateType::kInterpretedFunction,
                  bytecode_array->parameter_count(), bytecode_array->register_count(),
                  shared_info))
            , bytecode_iterator_(nullptr)
            , bytecode_analysis_(nullptr)
            , environment_(nullptr)
            , osr_offset_(osr_offset)
            , currently_peeled_loop_offset_(-1)
            , stack_check_(stack_check)
            , analyze_environment_liveness_(analyze_environment_liveness)
            , merge_environments_(local_zone)
            , generator_merge_environments_(local_zone)
            , exception_handlers_(local_zone)
            , current_exception_handler_(0)
            , input_buffer_size_(0)
            , input_buffer_(nullptr)
            , needs_eager_checkpoint_(true)
            , exit_controls_(local_zone)
            , state_values_cache_(jsgraph)
            , source_positions_(source_positions)
            , start_position_(shared_info->StartPosition(), inlining_id)
            , shared_info_(shared_info)
            , native_context_(native_context)
        {
        }

        Node* BytecodeGraphBuilder::GetFunctionClosure()
        {
            if (!function_closure_.is_set()) {
                int index = Linkage::kJSCallClosureParamIndex;
                const Operator* op = common()->Parameter(index, "%closure");
                Node* node = NewNode(op, graph()->start());
                function_closure_.set(node);
            }
            return function_closure_.get();
        }

        Node* BytecodeGraphBuilder::BuildLoadNativeContextField(int index)
        {
            const Operator* op = javascript()->LoadContext(0, Context::NATIVE_CONTEXT_INDEX, true);
            Node* native_context = NewNode(op);
            Node* result = NewNode(javascript()->LoadContext(0, index, true));
            NodeProperties::ReplaceContextInput(result, native_context);
            return result;
        }

        VectorSlotPair BytecodeGraphBuilder::CreateVectorSlotPair(int slot_id)
        {
            FeedbackSlot slot = FeedbackVector::ToSlot(slot_id);
            FeedbackNexus nexus(feedback_vector(), slot);
            return VectorSlotPair(feedback_vector(), slot, nexus.ic_state());
        }

        void BytecodeGraphBuilder::CreateGraph()
        {
            SourcePositionTable::Scope pos_scope(source_positions_, start_position_);

            // Set up the basic structure of the graph. Outputs for {Start} are the formal
            // parameters (including the receiver) plus new target, number of arguments,
            // context and closure.
            int actual_parameter_count = bytecode_array()->parameter_count() + 4;
            graph()->SetStart(graph()->NewNode(common()->Start(actual_parameter_count)));

            Environment env(this, bytecode_array()->register_count(),
                bytecode_array()->parameter_count(),
                bytecode_array()->incoming_new_target_or_generator_register(),
                graph()->start());
            set_environment(&env);

            VisitBytecodes();

            // Finish the basic structure of the graph.
            DCHECK_NE(0u, exit_controls_.size());
            int const input_count = static_cast<int>(exit_controls_.size());
            Node** const inputs = &exit_controls_.front();
            Node* end = graph()->NewNode(common()->End(input_count), input_count, inputs);
            graph()->SetEnd(end);
        }

        void BytecodeGraphBuilder::PrepareEagerCheckpoint()
        {
            if (needs_eager_checkpoint()) {
                // Create an explicit checkpoint node for before the operation. This only
                // needs to happen if we aren't effect-dominated by a {Checkpoint} already.
                mark_as_needing_eager_checkpoint(false);
                Node* node = NewNode(common()->Checkpoint());
                DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op()));
                DCHECK_EQ(IrOpcode::kDead,
                    NodeProperties::GetFrameStateInput(node)->opcode());
                BailoutId bailout_id(bytecode_iterator().current_offset());

                const BytecodeLivenessState* liveness_before = bytecode_analysis()->GetInLivenessFor(
                    bytecode_iterator().current_offset());

                Node* frame_state_before = environment()->Checkpoint(
                    bailout_id, OutputFrameStateCombine::Ignore(), liveness_before);
                NodeProperties::ReplaceFrameStateInput(node, frame_state_before);
#ifdef DEBUG
            } else {
                // In case we skipped checkpoint creation above, we must be able to find an
                // existing checkpoint that effect-dominates the nodes about to be created.
                // Starting a search from the current effect-dependency has to succeed.
                Node* effect = environment()->GetEffectDependency();
                while (effect->opcode() != IrOpcode::kCheckpoint) {
                    DCHECK(effect->op()->HasProperty(Operator::kNoWrite));
                    DCHECK_EQ(1, effect->op()->EffectInputCount());
                    effect = NodeProperties::GetEffectInput(effect);
                }
            }
#else
            }
#endif // DEBUG
        }

        void BytecodeGraphBuilder::PrepareFrameState(Node* node,
            OutputFrameStateCombine combine)
        {
            if (OperatorProperties::HasFrameStateInput(node->op())) {
                // Add the frame state for after the operation. The node in question has
                // already been created and had a {Dead} frame state input up until now.
                DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op()));
                DCHECK_EQ(IrOpcode::kDead,
                    NodeProperties::GetFrameStateInput(node)->opcode());
                BailoutId bailout_id(bytecode_iterator().current_offset());

                const BytecodeLivenessState* liveness_after = bytecode_analysis()->GetOutLivenessFor(
                    bytecode_iterator().current_offset());

                Node* frame_state_after = environment()->Checkpoint(bailout_id, combine, liveness_after);
                NodeProperties::ReplaceFrameStateInput(node, frame_state_after);
            }
        }

        // Stores the state of the SourcePosition iterator, and the index to the
        // current exception handlers stack. We need, during the OSR graph generation,
        // to backup the states of these iterators at the LoopHeader offset of each
        // outer loop which contains the OSR loop. The iterators are then restored when
        // peeling the loops, so that both exception handling and synchronisation with
        // the source position can be achieved.
        class BytecodeGraphBuilder::OsrIteratorState {
        public:
            OsrIteratorState(interpreter::BytecodeArrayIterator* iterator,
                SourcePositionTableIterator* source_position_iterator,
                BytecodeGraphBuilder* graph_builder)
                : iterator_(iterator)
                , source_position_iterator_(source_position_iterator)
                , graph_builder_(graph_builder)
                , saved_states_(graph_builder->local_zone())
            {
            }

            void ProcessOsrPrelude()
            {
                ZoneVector<int> outer_loop_offsets(graph_builder_->local_zone());

                const BytecodeAnalysis& bytecode_analysis = *(graph_builder_->bytecode_analysis());
                int osr_offset = bytecode_analysis.osr_entry_point();

                // We find here the outermost loop which contains the OSR loop.
                int outermost_loop_offset = osr_offset;
                while ((outermost_loop_offset = bytecode_analysis.GetLoopInfoFor(outermost_loop_offset)
                                                    .parent_offset())
                    != -1) {
                    outer_loop_offsets.push_back(outermost_loop_offset);
                }
                outermost_loop_offset = outer_loop_offsets.empty() ? osr_offset : outer_loop_offsets.back();

                // We will not processs any bytecode before the outermost_loop_offset, but
                // the source_position_iterator needs to be advanced step by step through
                // the bytecode.
                for (; iterator_->current_offset() != outermost_loop_offset;
                     iterator_->Advance()) {
                    graph_builder_->UpdateSourcePosition(source_position_iterator_,
                        iterator_->current_offset());
                }

                // We save some iterators states at the offsets of the loop headers of the
                // outer loops (the ones containing the OSR loop). They will be used for
                // jumping back in the bytecode.
                for (ZoneVector<int>::const_reverse_iterator it = outer_loop_offsets.crbegin();
                     it != outer_loop_offsets.crend(); ++it) {
                    int next_loop_offset = *it;
                    for (; iterator_->current_offset() != next_loop_offset;
                         iterator_->Advance()) {
                        graph_builder_->UpdateSourcePosition(source_position_iterator_,
                            iterator_->current_offset());
                    }
                    graph_builder_->ExitThenEnterExceptionHandlers(
                        iterator_->current_offset());
                    saved_states_.push(
                        IteratorsStates(graph_builder_->current_exception_handler(),
                            source_position_iterator_->GetState()));
                }

                // Finishing by advancing to the OSR entry
                for (; iterator_->current_offset() != osr_offset; iterator_->Advance()) {
                    graph_builder_->UpdateSourcePosition(source_position_iterator_,
                        iterator_->current_offset());
                }

                // Enters all remaining exception handler which end before the OSR loop
                // so that on next call of VisitSingleBytecode they will get popped from
                // the exception handlers stack.
                graph_builder_->ExitThenEnterExceptionHandlers(osr_offset);
                graph_builder_->set_currently_peeled_loop_offset(
                    bytecode_analysis.GetLoopInfoFor(osr_offset).parent_offset());
            }

            void RestoreState(int target_offset, int new_parent_offset)
            {
                iterator_->SetOffset(target_offset);
                // In case of a return, we must not build loop exits for
                // not-yet-built outer loops.
                graph_builder_->set_currently_peeled_loop_offset(new_parent_offset);
                IteratorsStates saved_state = saved_states_.top();
                source_position_iterator_->RestoreState(saved_state.source_iterator_state_);
                graph_builder_->set_current_exception_handler(
                    saved_state.exception_handler_index_);
                saved_states_.pop();
            }

        private:
            struct IteratorsStates {
                int exception_handler_index_;
                SourcePositionTableIterator::IndexAndPositionState source_iterator_state_;

                IteratorsStates(int exception_handler_index,
                    SourcePositionTableIterator::IndexAndPositionState
                        source_iterator_state)
                    : exception_handler_index_(exception_handler_index)
                    , source_iterator_state_(source_iterator_state)
                {
                }
            };

            interpreter::BytecodeArrayIterator* iterator_;
            SourcePositionTableIterator* source_position_iterator_;
            BytecodeGraphBuilder* graph_builder_;
            ZoneStack<IteratorsStates> saved_states_;
        };

        void BytecodeGraphBuilder::RemoveMergeEnvironmentsBeforeOffset(
            int limit_offset)
        {
            if (!merge_environments_.empty()) {
                ZoneMap<int, Environment*>::iterator it = merge_environments_.begin();
                ZoneMap<int, Environment*>::iterator stop_it = merge_environments_.end();
                while (it != stop_it && it->first <= limit_offset) {
                    it = merge_environments_.erase(it);
                }
            }
        }

        // We will iterate through the OSR loop, then its parent, and so on
        // until we have reached the outmost loop containing the OSR loop. We do
        // not generate nodes for anything before the outermost loop.
        void BytecodeGraphBuilder::AdvanceToOsrEntryAndPeelLoops(
            interpreter::BytecodeArrayIterator* iterator,
            SourcePositionTableIterator* source_position_iterator)
        {
            const BytecodeAnalysis& analysis = *(bytecode_analysis());
            int osr_offset = analysis.osr_entry_point();
            OsrIteratorState iterator_states(iterator, source_position_iterator, this);

            iterator_states.ProcessOsrPrelude();
            DCHECK_EQ(iterator->current_offset(), osr_offset);

            environment()->FillWithOsrValues();

            // Suppose we have n nested loops, loop_0 being the outermost one, and
            // loop_n being the OSR loop. We start iterating the bytecode at the header
            // of loop_n (the OSR loop), and then we peel the part of the the body of
            // loop_{n-1} following the end of loop_n. We then rewind the iterator to
            // the header of loop_{n-1}, and so on until we have partly peeled loop 0.
            // The full loop_0 body will be generating with the rest of the function,
            // outside the OSR generation.

            // To do so, if we are visiting a loop, we continue to visit what's left
            // of its parent, and then when reaching the parent's JumpLoop, we do not
            // create any jump for that but rewind the bytecode iterator to visit the
            // parent loop entirely, and so on.

            int current_parent_offset = analysis.GetLoopInfoFor(osr_offset).parent_offset();
            while (current_parent_offset != -1) {
                const LoopInfo& current_parent_loop = analysis.GetLoopInfoFor(current_parent_offset);
                // We iterate until the back edge of the parent loop, which we detect by
                // the offset that the JumpLoop targets.
                for (; !iterator->done(); iterator->Advance()) {
                    if (iterator->current_bytecode() == interpreter::Bytecode::kJumpLoop && iterator->GetJumpTargetOffset() == current_parent_offset) {
                        // Reached the end of the current parent loop.
                        break;
                    }
                    VisitSingleBytecode(source_position_iterator);
                }
                DCHECK(!iterator->done()); // Should have found the loop's jump target.

                // We also need to take care of the merge environments and exceptions
                // handlers here because the omitted JumpLoop bytecode can still be the
                // target of jumps or the first bytecode after a try block.
                ExitThenEnterExceptionHandlers(iterator->current_offset());
                SwitchToMergeEnvironment(iterator->current_offset());

                // This jump is the jump of our parent loop, which is not yet created.
                // So we do not build the jump nodes, but restore the bytecode and the
                // SourcePosition iterators to the values they had when we were visiting
                // the offset pointed at by the JumpLoop we've just reached.
                // We have already built nodes for inner loops, but now we will
                // iterate again over them and build new nodes corresponding to the same
                // bytecode offsets. Any jump or reference to this inner loops must now
                // point to the new nodes we will build, hence we clear the relevant part
                // of the environment.
                // Completely clearing the environment is not possible because merge
                // environments for forward jumps out of the loop need to be preserved
                // (e.g. a return or a labeled break in the middle of a loop).
                RemoveMergeEnvironmentsBeforeOffset(iterator->current_offset());
                iterator_states.RestoreState(current_parent_offset,
                    current_parent_loop.parent_offset());
                current_parent_offset = current_parent_loop.parent_offset();
            }
        }

        void BytecodeGraphBuilder::VisitSingleBytecode(
            SourcePositionTableIterator* source_position_iterator)
        {
            const interpreter::BytecodeArrayIterator& iterator = bytecode_iterator();
            int current_offset = iterator.current_offset();
            UpdateSourcePosition(source_position_iterator, current_offset);
            ExitThenEnterExceptionHandlers(current_offset);
            DCHECK_GE(exception_handlers_.empty() ? current_offset
                                                  : exception_handlers_.top().end_offset_,
                current_offset);
            SwitchToMergeEnvironment(current_offset);

            if (environment() != nullptr) {
                BuildLoopHeaderEnvironment(current_offset);

                // Skip the first stack check if stack_check is false
                if (!stack_check() && iterator.current_bytecode() == interpreter::Bytecode::kStackCheck) {
                    set_stack_check(true);
                    return;
                }

                switch (iterator.current_bytecode()) {
#define BYTECODE_CASE(name, ...)         \
    case interpreter::Bytecode::k##name: \
        Visit##name();                   \
        break;
                    BYTECODE_LIST(BYTECODE_CASE)
#undef BYTECODE_CASE
                }
            }
        }

        void BytecodeGraphBuilder::VisitBytecodes()
        {
            BytecodeAnalysis bytecode_analysis(bytecode_array(), local_zone(),
                analyze_environment_liveness());
            bytecode_analysis.Analyze(osr_offset_);
            set_bytecode_analysis(&bytecode_analysis);

            interpreter::BytecodeArrayIterator iterator(bytecode_array());
            set_bytecode_iterator(&iterator);
            SourcePositionTableIterator source_position_iterator(
                handle(bytecode_array()->SourcePositionTableIfCollected(), isolate()));

            if (analyze_environment_liveness() && FLAG_trace_environment_liveness) {
                StdoutStream of;
                bytecode_analysis.PrintLivenessTo(of);
            }

            if (!bytecode_analysis.resume_jump_targets().empty()) {
                environment()->BindGeneratorState(
                    jsgraph()->SmiConstant(JSGeneratorObject::kGeneratorExecuting));
            }

            if (bytecode_analysis.HasOsrEntryPoint()) {
                // We peel the OSR loop and any outer loop containing it except that we
                // leave the nodes corresponding to the whole outermost loop (including
                // the last copies of the loops it contains) to be generated by the normal
                // bytecode iteration below.
                AdvanceToOsrEntryAndPeelLoops(&iterator, &source_position_iterator);
            }

            bool has_one_shot_bytecode = false;
            for (; !iterator.done(); iterator.Advance()) {
                if (interpreter::Bytecodes::IsOneShotBytecode(
                        iterator.current_bytecode())) {
                    has_one_shot_bytecode = true;
                }
                VisitSingleBytecode(&source_position_iterator);
            }

            if (has_one_shot_bytecode) {
                isolate()->CountUsage(
                    v8::Isolate::UseCounterFeature::kOptimizedFunctionWithOneShotBytecode);
            }

            set_bytecode_analysis(nullptr);
            set_bytecode_iterator(nullptr);
            DCHECK(exception_handlers_.empty());
        }

        void BytecodeGraphBuilder::VisitLdaZero()
        {
            Node* node = jsgraph()->ZeroConstant();
            environment()->BindAccumulator(node);
        }

        void BytecodeGraphBuilder::VisitLdaSmi()
        {
            Node* node = jsgraph()->Constant(bytecode_iterator().GetImmediateOperand(0));
            environment()->BindAccumulator(node);
        }

        void BytecodeGraphBuilder::VisitLdaConstant()
        {
            Node* node = jsgraph()->Constant(
                handle(bytecode_iterator().GetConstantForIndexOperand(0), isolate()));
            environment()->BindAccumulator(node);
        }

        void BytecodeGraphBuilder::VisitLdaUndefined()
        {
            Node* node = jsgraph()->UndefinedConstant();
            environment()->BindAccumulator(node);
        }

        void BytecodeGraphBuilder::VisitLdaNull()
        {
            Node* node = jsgraph()->NullConstant();
            environment()->BindAccumulator(node);
        }

        void BytecodeGraphBuilder::VisitLdaTheHole()
        {
            Node* node = jsgraph()->TheHoleConstant();
            environment()->BindAccumulator(node);
        }

        void BytecodeGraphBuilder::VisitLdaTrue()
        {
            Node* node = jsgraph()->TrueConstant();
            environment()->BindAccumulator(node);
        }

        void BytecodeGraphBuilder::VisitLdaFalse()
        {
            Node* node = jsgraph()->FalseConstant();
            environment()->BindAccumulator(node);
        }

        void BytecodeGraphBuilder::VisitLdar()
        {
            Node* value = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            environment()->BindAccumulator(value);
        }

        void BytecodeGraphBuilder::VisitStar()
        {
            Node* value = environment()->LookupAccumulator();
            environment()->BindRegister(bytecode_iterator().GetRegisterOperand(0), value);
        }

        void BytecodeGraphBuilder::VisitMov()
        {
            Node* value = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            environment()->BindRegister(bytecode_iterator().GetRegisterOperand(1), value);
        }

        Node* BytecodeGraphBuilder::BuildLoadGlobal(Handle<Name> name,
            uint32_t feedback_slot_index,
            TypeofMode typeof_mode)
        {
            VectorSlotPair feedback = CreateVectorSlotPair(feedback_slot_index);
            DCHECK(IsLoadGlobalICKind(feedback_vector()->GetKind(feedback.slot())));
            const Operator* op = javascript()->LoadGlobal(name, feedback, typeof_mode);
            return NewNode(op);
        }

        void BytecodeGraphBuilder::VisitLdaGlobal()
        {
            PrepareEagerCheckpoint();
            Handle<Name> name(
                Name::cast(bytecode_iterator().GetConstantForIndexOperand(0)), isolate());
            uint32_t feedback_slot_index = bytecode_iterator().GetIndexOperand(1);
            Node* node = BuildLoadGlobal(name, feedback_slot_index, TypeofMode::NOT_INSIDE_TYPEOF);
            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitLdaGlobalInsideTypeof()
        {
            PrepareEagerCheckpoint();
            Handle<Name> name(
                Name::cast(bytecode_iterator().GetConstantForIndexOperand(0)), isolate());
            uint32_t feedback_slot_index = bytecode_iterator().GetIndexOperand(1);
            Node* node = BuildLoadGlobal(name, feedback_slot_index, TypeofMode::INSIDE_TYPEOF);
            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitStaGlobal()
        {
            PrepareEagerCheckpoint();
            Handle<Name> name(
                Name::cast(bytecode_iterator().GetConstantForIndexOperand(0)), isolate());
            VectorSlotPair feedback = CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(1));
            Node* value = environment()->LookupAccumulator();

            LanguageMode language_mode = feedback.vector()->GetLanguageMode(feedback.slot());
            const Operator* op = javascript()->StoreGlobal(language_mode, name, feedback);
            Node* node = NewNode(op, value);
            environment()->RecordAfterState(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitStaInArrayLiteral()
        {
            PrepareEagerCheckpoint();
            Node* value = environment()->LookupAccumulator();
            Node* array = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Node* index = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
            VectorSlotPair feedback = CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(2));
            const Operator* op = javascript()->StoreInArrayLiteral(feedback);

            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedStoreKeyed(op, array, index, value, feedback.slot());
            if (lowering.IsExit())
                return;

            Node* node = nullptr;
            if (lowering.IsSideEffectFree()) {
                node = lowering.value();
            } else {
                DCHECK(!lowering.Changed());
                node = NewNode(op, array, index, value);
            }

            environment()->RecordAfterState(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitStaDataPropertyInLiteral()
        {
            PrepareEagerCheckpoint();

            Node* object = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Node* name = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
            Node* value = environment()->LookupAccumulator();
            int flags = bytecode_iterator().GetFlagOperand(2);
            VectorSlotPair feedback = CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(3));

            const Operator* op = javascript()->StoreDataPropertyInLiteral(feedback);
            Node* node = NewNode(op, object, name, value, jsgraph()->Constant(flags));
            environment()->RecordAfterState(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitCollectTypeProfile()
        {
            PrepareEagerCheckpoint();

            Node* position = jsgraph()->Constant(bytecode_iterator().GetImmediateOperand(0));
            Node* value = environment()->LookupAccumulator();
            Node* vector = jsgraph()->Constant(feedback_vector());

            const Operator* op = javascript()->CallRuntime(Runtime::kCollectTypeProfile);

            Node* node = NewNode(op, position, value, vector);
            environment()->RecordAfterState(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitLdaContextSlot()
        {
            const Operator* op = javascript()->LoadContext(
                bytecode_iterator().GetUnsignedImmediateOperand(2),
                bytecode_iterator().GetIndexOperand(1), false);
            Node* node = NewNode(op);
            Node* context = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            NodeProperties::ReplaceContextInput(node, context);
            environment()->BindAccumulator(node);
        }

        void BytecodeGraphBuilder::VisitLdaImmutableContextSlot()
        {
            const Operator* op = javascript()->LoadContext(
                bytecode_iterator().GetUnsignedImmediateOperand(2),
                bytecode_iterator().GetIndexOperand(1), true);
            Node* node = NewNode(op);
            Node* context = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            NodeProperties::ReplaceContextInput(node, context);
            environment()->BindAccumulator(node);
        }

        void BytecodeGraphBuilder::VisitLdaCurrentContextSlot()
        {
            const Operator* op = javascript()->LoadContext(
                0, bytecode_iterator().GetIndexOperand(0), false);
            Node* node = NewNode(op);
            environment()->BindAccumulator(node);
        }

        void BytecodeGraphBuilder::VisitLdaImmutableCurrentContextSlot()
        {
            const Operator* op = javascript()->LoadContext(
                0, bytecode_iterator().GetIndexOperand(0), true);
            Node* node = NewNode(op);
            environment()->BindAccumulator(node);
        }

        void BytecodeGraphBuilder::VisitStaContextSlot()
        {
            const Operator* op = javascript()->StoreContext(
                bytecode_iterator().GetUnsignedImmediateOperand(2),
                bytecode_iterator().GetIndexOperand(1));
            Node* value = environment()->LookupAccumulator();
            Node* node = NewNode(op, value);
            Node* context = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            NodeProperties::ReplaceContextInput(node, context);
        }

        void BytecodeGraphBuilder::VisitStaCurrentContextSlot()
        {
            const Operator* op = javascript()->StoreContext(0, bytecode_iterator().GetIndexOperand(0));
            Node* value = environment()->LookupAccumulator();
            NewNode(op, value);
        }

        void BytecodeGraphBuilder::BuildLdaLookupSlot(TypeofMode typeof_mode)
        {
            PrepareEagerCheckpoint();
            Node* name = jsgraph()->Constant(
                handle(bytecode_iterator().GetConstantForIndexOperand(0), isolate()));
            const Operator* op = javascript()->CallRuntime(typeof_mode == TypeofMode::NOT_INSIDE_TYPEOF
                    ? Runtime::kLoadLookupSlot
                    : Runtime::kLoadLookupSlotInsideTypeof);
            Node* value = NewNode(op, name);
            environment()->BindAccumulator(value, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitLdaLookupSlot()
        {
            BuildLdaLookupSlot(TypeofMode::NOT_INSIDE_TYPEOF);
        }

        void BytecodeGraphBuilder::VisitLdaLookupSlotInsideTypeof()
        {
            BuildLdaLookupSlot(TypeofMode::INSIDE_TYPEOF);
        }

        BytecodeGraphBuilder::Environment* BytecodeGraphBuilder::CheckContextExtensions(
            uint32_t depth)
        {
            // Output environment where the context has an extension
            Environment* slow_environment = nullptr;

            // We only need to check up to the last-but-one depth, because the an eval
            // in the same scope as the variable itself has no way of shadowing it.
            for (uint32_t d = 0; d < depth; d++) {
                Node* extension_slot = NewNode(javascript()->LoadContext(d, Context::EXTENSION_INDEX, false));

                Node* check_no_extension = NewNode(simplified()->ReferenceEqual(), extension_slot,
                    jsgraph()->TheHoleConstant());

                NewBranch(check_no_extension);

                {
                    SubEnvironment sub_environment(this);

                    NewIfFalse();
                    // If there is an extension, merge into the slow path.
                    if (slow_environment == nullptr) {
                        slow_environment = environment();
                        NewMerge();
                    } else {
                        slow_environment->Merge(environment(),
                            bytecode_analysis()->GetInLivenessFor(
                                bytecode_iterator().current_offset()));
                    }
                }

                NewIfTrue();
                // Do nothing on if there is no extension, eventually falling through to
                // the fast path.
            }

            // The depth can be zero, in which case no slow-path checks are built, and
            // the slow path environment can be null.
            DCHECK(depth == 0 || slow_environment != nullptr);

            return slow_environment;
        }

        void BytecodeGraphBuilder::BuildLdaLookupContextSlot(TypeofMode typeof_mode)
        {
            uint32_t depth = bytecode_iterator().GetUnsignedImmediateOperand(2);

            // Check if any context in the depth has an extension.
            Environment* slow_environment = CheckContextExtensions(depth);

            // Fast path, do a context load.
            {
                uint32_t slot_index = bytecode_iterator().GetIndexOperand(1);

                const Operator* op = javascript()->LoadContext(depth, slot_index, false);
                environment()->BindAccumulator(NewNode(op));
            }

            // Only build the slow path if there were any slow-path checks.
            if (slow_environment != nullptr) {
                // Add a merge to the fast environment.
                NewMerge();
                Environment* fast_environment = environment();

                // Slow path, do a runtime load lookup.
                set_environment(slow_environment);
                {
                    Node* name = jsgraph()->Constant(
                        handle(bytecode_iterator().GetConstantForIndexOperand(0), isolate()));

                    const Operator* op = javascript()->CallRuntime(typeof_mode == TypeofMode::NOT_INSIDE_TYPEOF
                            ? Runtime::kLoadLookupSlot
                            : Runtime::kLoadLookupSlotInsideTypeof);
                    Node* value = NewNode(op, name);
                    environment()->BindAccumulator(value, Environment::kAttachFrameState);
                }

                fast_environment->Merge(environment(),
                    bytecode_analysis()->GetOutLivenessFor(
                        bytecode_iterator().current_offset()));
                set_environment(fast_environment);
                mark_as_needing_eager_checkpoint(true);
            }
        }

        void BytecodeGraphBuilder::VisitLdaLookupContextSlot()
        {
            BuildLdaLookupContextSlot(TypeofMode::NOT_INSIDE_TYPEOF);
        }

        void BytecodeGraphBuilder::VisitLdaLookupContextSlotInsideTypeof()
        {
            BuildLdaLookupContextSlot(TypeofMode::INSIDE_TYPEOF);
        }

        void BytecodeGraphBuilder::BuildLdaLookupGlobalSlot(TypeofMode typeof_mode)
        {
            uint32_t depth = bytecode_iterator().GetUnsignedImmediateOperand(2);

            // Check if any context in the depth has an extension.
            Environment* slow_environment = CheckContextExtensions(depth);

            // Fast path, do a global load.
            {
                PrepareEagerCheckpoint();
                Handle<Name> name(
                    Name::cast(bytecode_iterator().GetConstantForIndexOperand(0)),
                    isolate());
                uint32_t feedback_slot_index = bytecode_iterator().GetIndexOperand(1);
                Node* node = BuildLoadGlobal(name, feedback_slot_index, typeof_mode);
                environment()->BindAccumulator(node, Environment::kAttachFrameState);
            }

            // Only build the slow path if there were any slow-path checks.
            if (slow_environment != nullptr) {
                // Add a merge to the fast environment.
                NewMerge();
                Environment* fast_environment = environment();

                // Slow path, do a runtime load lookup.
                set_environment(slow_environment);
                {
                    Node* name = jsgraph()->Constant(
                        handle(bytecode_iterator().GetConstantForIndexOperand(0), isolate()));

                    const Operator* op = javascript()->CallRuntime(typeof_mode == TypeofMode::NOT_INSIDE_TYPEOF
                            ? Runtime::kLoadLookupSlot
                            : Runtime::kLoadLookupSlotInsideTypeof);
                    Node* value = NewNode(op, name);
                    environment()->BindAccumulator(value, Environment::kAttachFrameState);
                }

                fast_environment->Merge(environment(),
                    bytecode_analysis()->GetOutLivenessFor(
                        bytecode_iterator().current_offset()));
                set_environment(fast_environment);
                mark_as_needing_eager_checkpoint(true);
            }
        }

        void BytecodeGraphBuilder::VisitLdaLookupGlobalSlot()
        {
            BuildLdaLookupGlobalSlot(TypeofMode::NOT_INSIDE_TYPEOF);
        }

        void BytecodeGraphBuilder::VisitLdaLookupGlobalSlotInsideTypeof()
        {
            BuildLdaLookupGlobalSlot(TypeofMode::INSIDE_TYPEOF);
        }

        void BytecodeGraphBuilder::VisitStaLookupSlot()
        {
            PrepareEagerCheckpoint();
            Node* value = environment()->LookupAccumulator();
            Node* name = jsgraph()->Constant(
                handle(bytecode_iterator().GetConstantForIndexOperand(0), isolate()));
            int bytecode_flags = bytecode_iterator().GetFlagOperand(1);
            LanguageMode language_mode = static_cast<LanguageMode>(
                interpreter::StoreLookupSlotFlags::LanguageModeBit::decode(
                    bytecode_flags));
            LookupHoistingMode lookup_hoisting_mode = static_cast<LookupHoistingMode>(
                interpreter::StoreLookupSlotFlags::LookupHoistingModeBit::decode(
                    bytecode_flags));
            DCHECK_IMPLIES(lookup_hoisting_mode == LookupHoistingMode::kLegacySloppy,
                is_sloppy(language_mode));
            const Operator* op = javascript()->CallRuntime(
                is_strict(language_mode)
                    ? Runtime::kStoreLookupSlot_Strict
                    : lookup_hoisting_mode == LookupHoistingMode::kLegacySloppy
                        ? Runtime::kStoreLookupSlot_SloppyHoisting
                        : Runtime::kStoreLookupSlot_Sloppy);
            Node* store = NewNode(op, name, value);
            environment()->BindAccumulator(store, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitLdaNamedProperty()
        {
            PrepareEagerCheckpoint();
            Node* object = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Handle<Name> name(
                Name::cast(bytecode_iterator().GetConstantForIndexOperand(1)), isolate());
            VectorSlotPair feedback = CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(2));
            const Operator* op = javascript()->LoadNamed(name, feedback);

            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedLoadNamed(op, object, feedback.slot());
            if (lowering.IsExit())
                return;

            Node* node = nullptr;
            if (lowering.IsSideEffectFree()) {
                node = lowering.value();
            } else {
                DCHECK(!lowering.Changed());
                node = NewNode(op, object);
            }
            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitLdaNamedPropertyNoFeedback()
        {
            PrepareEagerCheckpoint();
            Node* object = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Handle<Name> name(
                Name::cast(bytecode_iterator().GetConstantForIndexOperand(1)), isolate());
            const Operator* op = javascript()->LoadNamed(name, VectorSlotPair());
            Node* node = NewNode(op, object);
            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitLdaKeyedProperty()
        {
            PrepareEagerCheckpoint();
            Node* key = environment()->LookupAccumulator();
            Node* object = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            VectorSlotPair feedback = CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(1));
            const Operator* op = javascript()->LoadProperty(feedback);

            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedLoadKeyed(op, object, key, feedback.slot());
            if (lowering.IsExit())
                return;

            Node* node = nullptr;
            if (lowering.IsSideEffectFree()) {
                node = lowering.value();
            } else {
                DCHECK(!lowering.Changed());
                node = NewNode(op, object, key);
            }
            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::BuildNamedStore(StoreMode store_mode)
        {
            PrepareEagerCheckpoint();
            Node* value = environment()->LookupAccumulator();
            Node* object = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Handle<Name> name(
                Name::cast(bytecode_iterator().GetConstantForIndexOperand(1)), isolate());
            VectorSlotPair feedback = CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(2));

            const Operator* op;
            if (store_mode == StoreMode::kOwn) {
                DCHECK_EQ(FeedbackSlotKind::kStoreOwnNamed,
                    feedback.vector()->GetKind(feedback.slot()));
                op = javascript()->StoreNamedOwn(name, feedback);
            } else {
                DCHECK_EQ(StoreMode::kNormal, store_mode);
                LanguageMode language_mode = feedback.vector()->GetLanguageMode(feedback.slot());
                op = javascript()->StoreNamed(language_mode, name, feedback);
            }

            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedStoreNamed(op, object, value, feedback.slot());
            if (lowering.IsExit())
                return;

            Node* node = nullptr;
            if (lowering.IsSideEffectFree()) {
                node = lowering.value();
            } else {
                DCHECK(!lowering.Changed());
                node = NewNode(op, object, value);
            }
            environment()->RecordAfterState(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitStaNamedProperty()
        {
            BuildNamedStore(StoreMode::kNormal);
        }

        void BytecodeGraphBuilder::VisitStaNamedPropertyNoFeedback()
        {
            PrepareEagerCheckpoint();
            Node* value = environment()->LookupAccumulator();
            Node* object = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Handle<Name> name(
                Name::cast(bytecode_iterator().GetConstantForIndexOperand(1)), isolate());
            LanguageMode language_mode = static_cast<LanguageMode>(bytecode_iterator().GetFlagOperand(2));
            const Operator* op = javascript()->StoreNamed(language_mode, name, VectorSlotPair());
            Node* node = NewNode(op, object, value);
            environment()->RecordAfterState(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitStaNamedOwnProperty()
        {
            BuildNamedStore(StoreMode::kOwn);
        }

        void BytecodeGraphBuilder::VisitStaKeyedProperty()
        {
            PrepareEagerCheckpoint();
            Node* value = environment()->LookupAccumulator();
            Node* object = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Node* key = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
            VectorSlotPair feedback = CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(2));
            LanguageMode language_mode = feedback.vector()->GetLanguageMode(feedback.slot());
            const Operator* op = javascript()->StoreProperty(language_mode, feedback);

            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedStoreKeyed(op, object, key, value, feedback.slot());
            if (lowering.IsExit())
                return;

            Node* node = nullptr;
            if (lowering.IsSideEffectFree()) {
                node = lowering.value();
            } else {
                DCHECK(!lowering.Changed());
                node = NewNode(op, object, key, value);
            }

            environment()->RecordAfterState(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitLdaModuleVariable()
        {
            int32_t cell_index = bytecode_iterator().GetImmediateOperand(0);
            uint32_t depth = bytecode_iterator().GetUnsignedImmediateOperand(1);
            Node* module = NewNode(javascript()->LoadContext(depth, Context::EXTENSION_INDEX, true));
            Node* value = NewNode(javascript()->LoadModule(cell_index), module);
            environment()->BindAccumulator(value);
        }

        void BytecodeGraphBuilder::VisitStaModuleVariable()
        {
            int32_t cell_index = bytecode_iterator().GetImmediateOperand(0);
            uint32_t depth = bytecode_iterator().GetUnsignedImmediateOperand(1);
            Node* module = NewNode(javascript()->LoadContext(depth, Context::EXTENSION_INDEX, true));
            Node* value = environment()->LookupAccumulator();
            NewNode(javascript()->StoreModule(cell_index), module, value);
        }

        void BytecodeGraphBuilder::VisitPushContext()
        {
            Node* new_context = environment()->LookupAccumulator();
            environment()->BindRegister(bytecode_iterator().GetRegisterOperand(0),
                environment()->Context());
            environment()->SetContext(new_context);
        }

        void BytecodeGraphBuilder::VisitPopContext()
        {
            Node* context = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            environment()->SetContext(context);
        }

        void BytecodeGraphBuilder::VisitCreateClosure()
        {
            Handle<SharedFunctionInfo> shared_info(
                SharedFunctionInfo::cast(
                    bytecode_iterator().GetConstantForIndexOperand(0)),
                isolate());
            AllocationType allocation = interpreter::CreateClosureFlags::PretenuredBit::decode(
                                            bytecode_iterator().GetFlagOperand(2))
                ? AllocationType::kOld
                : AllocationType::kYoung;
            const Operator* op = javascript()->CreateClosure(
                shared_info,
                feedback_vector()->GetClosureFeedbackCell(
                    bytecode_iterator().GetIndexOperand(1)),
                handle(jsgraph()->isolate()->builtins()->builtin(Builtins::kCompileLazy),
                    isolate()),
                allocation);
            Node* closure = NewNode(op);
            environment()->BindAccumulator(closure);
        }

        void BytecodeGraphBuilder::VisitCreateBlockContext()
        {
            Handle<ScopeInfo> scope_info(
                ScopeInfo::cast(bytecode_iterator().GetConstantForIndexOperand(0)),
                isolate());

            const Operator* op = javascript()->CreateBlockContext(scope_info);
            Node* context = NewNode(op);
            environment()->BindAccumulator(context);
        }

        void BytecodeGraphBuilder::VisitCreateFunctionContext()
        {
            Handle<ScopeInfo> scope_info(
                ScopeInfo::cast(bytecode_iterator().GetConstantForIndexOperand(0)),
                isolate());
            uint32_t slots = bytecode_iterator().GetUnsignedImmediateOperand(1);
            const Operator* op = javascript()->CreateFunctionContext(scope_info, slots, FUNCTION_SCOPE);
            Node* context = NewNode(op);
            environment()->BindAccumulator(context);
        }

        void BytecodeGraphBuilder::VisitCreateEvalContext()
        {
            Handle<ScopeInfo> scope_info(
                ScopeInfo::cast(bytecode_iterator().GetConstantForIndexOperand(0)),
                isolate());
            uint32_t slots = bytecode_iterator().GetUnsignedImmediateOperand(1);
            const Operator* op = javascript()->CreateFunctionContext(scope_info, slots, EVAL_SCOPE);
            Node* context = NewNode(op);
            environment()->BindAccumulator(context);
        }

        void BytecodeGraphBuilder::VisitCreateCatchContext()
        {
            interpreter::Register reg = bytecode_iterator().GetRegisterOperand(0);
            Node* exception = environment()->LookupRegister(reg);
            Handle<ScopeInfo> scope_info(
                ScopeInfo::cast(bytecode_iterator().GetConstantForIndexOperand(1)),
                isolate());

            const Operator* op = javascript()->CreateCatchContext(scope_info);
            Node* context = NewNode(op, exception);
            environment()->BindAccumulator(context);
        }

        void BytecodeGraphBuilder::VisitCreateWithContext()
        {
            Node* object = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Handle<ScopeInfo> scope_info(
                ScopeInfo::cast(bytecode_iterator().GetConstantForIndexOperand(1)),
                isolate());

            const Operator* op = javascript()->CreateWithContext(scope_info);
            Node* context = NewNode(op, object);
            environment()->BindAccumulator(context);
        }

        void BytecodeGraphBuilder::BuildCreateArguments(CreateArgumentsType type)
        {
            const Operator* op = javascript()->CreateArguments(type);
            Node* object = NewNode(op, GetFunctionClosure());
            environment()->BindAccumulator(object, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitCreateMappedArguments()
        {
            BuildCreateArguments(CreateArgumentsType::kMappedArguments);
        }

        void BytecodeGraphBuilder::VisitCreateUnmappedArguments()
        {
            BuildCreateArguments(CreateArgumentsType::kUnmappedArguments);
        }

        void BytecodeGraphBuilder::VisitCreateRestParameter()
        {
            BuildCreateArguments(CreateArgumentsType::kRestParameter);
        }

        void BytecodeGraphBuilder::VisitCreateRegExpLiteral()
        {
            Handle<String> constant_pattern(
                String::cast(bytecode_iterator().GetConstantForIndexOperand(0)),
                isolate());
            int const slot_id = bytecode_iterator().GetIndexOperand(1);
            VectorSlotPair pair = CreateVectorSlotPair(slot_id);
            int literal_flags = bytecode_iterator().GetFlagOperand(2);
            Node* literal = NewNode(
                javascript()->CreateLiteralRegExp(constant_pattern, pair, literal_flags));
            environment()->BindAccumulator(literal, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitCreateArrayLiteral()
        {
            Handle<ArrayBoilerplateDescription> array_boilerplate_description(
                ArrayBoilerplateDescription::cast(
                    bytecode_iterator().GetConstantForIndexOperand(0)),
                isolate());
            int const slot_id = bytecode_iterator().GetIndexOperand(1);
            VectorSlotPair pair = CreateVectorSlotPair(slot_id);
            int bytecode_flags = bytecode_iterator().GetFlagOperand(2);
            int literal_flags = interpreter::CreateArrayLiteralFlags::FlagsBits::decode(bytecode_flags);
            // Disable allocation site mementos. Only unoptimized code will collect
            // feedback about allocation site. Once the code is optimized we expect the
            // data to converge. So, we disable allocation site mementos in optimized
            // code. We can revisit this when we have data to the contrary.
            literal_flags |= ArrayLiteral::kDisableMementos;
            // TODO(mstarzinger): Thread through number of elements. The below number is
            // only an estimate and does not match {ArrayLiteral::values::length}.
            int number_of_elements = array_boilerplate_description->constant_elements()->length();
            Node* literal = NewNode(javascript()->CreateLiteralArray(
                array_boilerplate_description, pair, literal_flags, number_of_elements));
            environment()->BindAccumulator(literal, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitCreateEmptyArrayLiteral()
        {
            int const slot_id = bytecode_iterator().GetIndexOperand(0);
            VectorSlotPair pair = CreateVectorSlotPair(slot_id);
            Node* literal = NewNode(javascript()->CreateEmptyLiteralArray(pair));
            environment()->BindAccumulator(literal);
        }

        void BytecodeGraphBuilder::VisitCreateArrayFromIterable()
        {
            Node* iterable = NewNode(javascript()->CreateArrayFromIterable(),
                environment()->LookupAccumulator());
            environment()->BindAccumulator(iterable, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitCreateObjectLiteral()
        {
            Handle<ObjectBoilerplateDescription> constant_properties(
                ObjectBoilerplateDescription::cast(
                    bytecode_iterator().GetConstantForIndexOperand(0)),
                isolate());
            int const slot_id = bytecode_iterator().GetIndexOperand(1);
            VectorSlotPair pair = CreateVectorSlotPair(slot_id);
            int bytecode_flags = bytecode_iterator().GetFlagOperand(2);
            int literal_flags = interpreter::CreateObjectLiteralFlags::FlagsBits::decode(bytecode_flags);
            // TODO(mstarzinger): Thread through number of properties. The below number is
            // only an estimate and does not match {ObjectLiteral::properties_count}.
            int number_of_properties = constant_properties->size();
            Node* literal = NewNode(javascript()->CreateLiteralObject(
                constant_properties, pair, literal_flags, number_of_properties));
            environment()->BindAccumulator(literal, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitCreateEmptyObjectLiteral()
        {
            Node* literal = NewNode(javascript()->CreateEmptyLiteralObject(), GetFunctionClosure());
            environment()->BindAccumulator(literal);
        }

        void BytecodeGraphBuilder::VisitCloneObject()
        {
            PrepareEagerCheckpoint();
            Node* source = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            int flags = bytecode_iterator().GetFlagOperand(1);
            int slot = bytecode_iterator().GetIndexOperand(2);
            const Operator* op = javascript()->CloneObject(CreateVectorSlotPair(slot), flags);
            Node* value = NewNode(op, source);
            environment()->BindAccumulator(value, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitGetTemplateObject()
        {
            Handle<TemplateObjectDescription> description(
                TemplateObjectDescription::cast(
                    bytecode_iterator().GetConstantForIndexOperand(0)),
                isolate());
            FeedbackSlot slot = bytecode_iterator().GetSlotOperand(1);
            FeedbackNexus nexus(feedback_vector(), slot);

            Handle<JSArray> cached_value;
            if (nexus.GetFeedback() == MaybeObject::FromSmi(Smi::zero())) {
                // It's not observable when the template object is created, so we
                // can just create it eagerly during graph building and bake in
                // the JSArray constant here.
                cached_value = TemplateObjectDescription::GetTemplateObject(
                    isolate(), native_context(), description, shared_info(), slot.ToInt());
                nexus.vector()->Set(slot, *cached_value);
            } else {
                cached_value = handle(JSArray::cast(nexus.GetFeedback()->GetHeapObjectAssumeStrong()),
                    isolate());
            }

            Node* template_object = jsgraph()->HeapConstant(cached_value);
            environment()->BindAccumulator(template_object);
        }

        Node* const* BytecodeGraphBuilder::GetCallArgumentsFromRegisters(
            Node* callee, Node* receiver, interpreter::Register first_arg,
            int arg_count)
        {
            // The arity of the Call node -- includes the callee, receiver and function
            // arguments.
            int arity = 2 + arg_count;

            Node** all = local_zone()->NewArray<Node*>(static_cast<size_t>(arity));

            all[0] = callee;
            all[1] = receiver;

            // The function arguments are in consecutive registers.
            int arg_base = first_arg.index();
            for (int i = 0; i < arg_count; ++i) {
                all[2 + i] = environment()->LookupRegister(interpreter::Register(arg_base + i));
            }

            return all;
        }

        Node* BytecodeGraphBuilder::ProcessCallArguments(const Operator* call_op,
            Node* const* args,
            int arg_count)
        {
            return MakeNode(call_op, arg_count, args, false);
        }

        Node* BytecodeGraphBuilder::ProcessCallArguments(const Operator* call_op,
            Node* callee,
            interpreter::Register receiver,
            size_t reg_count)
        {
            Node* receiver_node = environment()->LookupRegister(receiver);
            // The receiver is followed by the arguments in the consecutive registers.
            DCHECK_GE(reg_count, 1);
            interpreter::Register first_arg = interpreter::Register(receiver.index() + 1);
            int arg_count = static_cast<int>(reg_count) - 1;

            Node* const* call_args = GetCallArgumentsFromRegisters(callee, receiver_node,
                first_arg, arg_count);
            return ProcessCallArguments(call_op, call_args, 2 + arg_count);
        }

        void BytecodeGraphBuilder::BuildCall(ConvertReceiverMode receiver_mode,
            Node* const* args, size_t arg_count,
            int slot_id)
        {
            DCHECK_EQ(interpreter::Bytecodes::GetReceiverMode(
                          bytecode_iterator().current_bytecode()),
                receiver_mode);
            PrepareEagerCheckpoint();

            VectorSlotPair feedback = CreateVectorSlotPair(slot_id);

            CallFrequency frequency = ComputeCallFrequency(slot_id);
            const Operator* op = javascript()->Call(arg_count, frequency, feedback, receiver_mode,
                GetSpeculationMode(slot_id));
            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedCall(
                op, args, static_cast<int>(arg_count), feedback.slot());
            if (lowering.IsExit())
                return;

            Node* node = nullptr;
            if (lowering.IsSideEffectFree()) {
                node = lowering.value();
            } else {
                DCHECK(!lowering.Changed());
                node = ProcessCallArguments(op, args, static_cast<int>(arg_count));
            }
            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        Node* const* BytecodeGraphBuilder::ProcessCallVarArgs(
            ConvertReceiverMode receiver_mode, Node* callee,
            interpreter::Register first_reg, int arg_count)
        {
            DCHECK_GE(arg_count, 0);
            Node* receiver_node;
            interpreter::Register first_arg;

            if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) {
                // The receiver is implicit (and undefined), the arguments are in
                // consecutive registers.
                receiver_node = jsgraph()->UndefinedConstant();
                first_arg = first_reg;
            } else {
                // The receiver is the first register, followed by the arguments in the
                // consecutive registers.
                receiver_node = environment()->LookupRegister(first_reg);
                first_arg = interpreter::Register(first_reg.index() + 1);
            }

            Node* const* call_args = GetCallArgumentsFromRegisters(callee, receiver_node,
                first_arg, arg_count);
            return call_args;
        }

        void BytecodeGraphBuilder::BuildCallVarArgs(ConvertReceiverMode receiver_mode)
        {
            DCHECK_EQ(interpreter::Bytecodes::GetReceiverMode(
                          bytecode_iterator().current_bytecode()),
                receiver_mode);
            Node* callee = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1);
            size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
            int const slot_id = bytecode_iterator().GetIndexOperand(3);

            int arg_count = receiver_mode == ConvertReceiverMode::kNullOrUndefined
                ? static_cast<int>(reg_count)
                : static_cast<int>(reg_count) - 1;
            Node* const* call_args = ProcessCallVarArgs(receiver_mode, callee, first_reg, arg_count);
            BuildCall(receiver_mode, call_args, static_cast<size_t>(2 + arg_count),
                slot_id);
        }

        void BytecodeGraphBuilder::VisitCallAnyReceiver()
        {
            BuildCallVarArgs(ConvertReceiverMode::kAny);
        }

        void BytecodeGraphBuilder::VisitCallNoFeedback()
        {
            DCHECK_EQ(interpreter::Bytecodes::GetReceiverMode(
                          bytecode_iterator().current_bytecode()),
                ConvertReceiverMode::kAny);

            PrepareEagerCheckpoint();
            Node* callee = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));

            interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1);
            size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);

            // The receiver is the first register, followed by the arguments in the
            // consecutive registers.
            int arg_count = static_cast<int>(reg_count) - 1;
            // The arity of the Call node -- includes the callee, receiver and function
            // arguments.
            int arity = 2 + arg_count;

            // Setting call frequency to a value less than min_inlining frequency to
            // prevent inlining of one-shot call node.
            DCHECK(CallFrequency::kNoFeedbackCallFrequency < FLAG_min_inlining_frequency);
            const Operator* call = javascript()->Call(
                arity, CallFrequency(CallFrequency::kNoFeedbackCallFrequency));
            Node* const* call_args = ProcessCallVarArgs(ConvertReceiverMode::kAny, callee,
                first_reg, arg_count);
            Node* value = ProcessCallArguments(call, call_args, arity);
            environment()->BindAccumulator(value, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitCallProperty()
        {
            BuildCallVarArgs(ConvertReceiverMode::kNotNullOrUndefined);
        }

        void BytecodeGraphBuilder::VisitCallProperty0()
        {
            Node* callee = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Node* receiver = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
            int const slot_id = bytecode_iterator().GetIndexOperand(2);
            BuildCall(ConvertReceiverMode::kNotNullOrUndefined, { callee, receiver },
                slot_id);
        }

        void BytecodeGraphBuilder::VisitCallProperty1()
        {
            Node* callee = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Node* receiver = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
            Node* arg0 = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(2));
            int const slot_id = bytecode_iterator().GetIndexOperand(3);
            BuildCall(ConvertReceiverMode::kNotNullOrUndefined, { callee, receiver, arg0 },
                slot_id);
        }

        void BytecodeGraphBuilder::VisitCallProperty2()
        {
            Node* callee = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Node* receiver = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
            Node* arg0 = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(2));
            Node* arg1 = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(3));
            int const slot_id = bytecode_iterator().GetIndexOperand(4);
            BuildCall(ConvertReceiverMode::kNotNullOrUndefined,
                { callee, receiver, arg0, arg1 }, slot_id);
        }

        void BytecodeGraphBuilder::VisitCallUndefinedReceiver()
        {
            BuildCallVarArgs(ConvertReceiverMode::kNullOrUndefined);
        }

        void BytecodeGraphBuilder::VisitCallUndefinedReceiver0()
        {
            Node* callee = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Node* receiver = jsgraph()->UndefinedConstant();
            int const slot_id = bytecode_iterator().GetIndexOperand(1);
            BuildCall(ConvertReceiverMode::kNullOrUndefined, { callee, receiver }, slot_id);
        }

        void BytecodeGraphBuilder::VisitCallUndefinedReceiver1()
        {
            Node* callee = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Node* receiver = jsgraph()->UndefinedConstant();
            Node* arg0 = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
            int const slot_id = bytecode_iterator().GetIndexOperand(2);
            BuildCall(ConvertReceiverMode::kNullOrUndefined, { callee, receiver, arg0 },
                slot_id);
        }

        void BytecodeGraphBuilder::VisitCallUndefinedReceiver2()
        {
            Node* callee = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Node* receiver = jsgraph()->UndefinedConstant();
            Node* arg0 = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
            Node* arg1 = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(2));
            int const slot_id = bytecode_iterator().GetIndexOperand(3);
            BuildCall(ConvertReceiverMode::kNullOrUndefined,
                { callee, receiver, arg0, arg1 }, slot_id);
        }

        void BytecodeGraphBuilder::VisitCallWithSpread()
        {
            PrepareEagerCheckpoint();
            Node* callee = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            interpreter::Register receiver = bytecode_iterator().GetRegisterOperand(1);
            Node* receiver_node = environment()->LookupRegister(receiver);
            size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
            interpreter::Register first_arg = interpreter::Register(receiver.index() + 1);
            int arg_count = static_cast<int>(reg_count) - 1;
            Node* const* args = GetCallArgumentsFromRegisters(callee, receiver_node,
                first_arg, arg_count);
            int const slot_id = bytecode_iterator().GetIndexOperand(3);
            VectorSlotPair feedback = CreateVectorSlotPair(slot_id);

            CallFrequency frequency = ComputeCallFrequency(slot_id);
            const Operator* op = javascript()->CallWithSpread(
                static_cast<int>(reg_count + 1), frequency, feedback);

            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedCall(
                op, args, static_cast<int>(arg_count), feedback.slot());
            if (lowering.IsExit())
                return;

            Node* node = nullptr;
            if (lowering.IsSideEffectFree()) {
                node = lowering.value();
            } else {
                DCHECK(!lowering.Changed());
                node = ProcessCallArguments(op, args, 2 + arg_count);
            }
            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitCallJSRuntime()
        {
            PrepareEagerCheckpoint();
            Node* callee = BuildLoadNativeContextField(
                bytecode_iterator().GetNativeContextIndexOperand(0));
            interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1);
            size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
            int arg_count = static_cast<int>(reg_count);

            const Operator* call = javascript()->Call(2 + arg_count);
            Node* const* call_args = ProcessCallVarArgs(
                ConvertReceiverMode::kNullOrUndefined, callee, first_reg, arg_count);
            Node* value = ProcessCallArguments(call, call_args, 2 + arg_count);
            environment()->BindAccumulator(value, Environment::kAttachFrameState);
        }

        Node* BytecodeGraphBuilder::ProcessCallRuntimeArguments(
            const Operator* call_runtime_op, interpreter::Register receiver,
            size_t reg_count)
        {
            int arg_count = static_cast<int>(reg_count);
            // arity is args.
            int arity = arg_count;
            Node** all = local_zone()->NewArray<Node*>(static_cast<size_t>(arity));
            int first_arg_index = receiver.index();
            for (int i = 0; i < static_cast<int>(reg_count); ++i) {
                all[i] = environment()->LookupRegister(
                    interpreter::Register(first_arg_index + i));
            }
            Node* value = MakeNode(call_runtime_op, arity, all, false);
            return value;
        }

        void BytecodeGraphBuilder::VisitCallRuntime()
        {
            PrepareEagerCheckpoint();
            Runtime::FunctionId function_id = bytecode_iterator().GetRuntimeIdOperand(0);
            interpreter::Register receiver = bytecode_iterator().GetRegisterOperand(1);
            size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);

            // Create node to perform the runtime call.
            const Operator* call = javascript()->CallRuntime(function_id, reg_count);
            Node* value = ProcessCallRuntimeArguments(call, receiver, reg_count);
            environment()->BindAccumulator(value, Environment::kAttachFrameState);

            // Connect to the end if {function_id} is non-returning.
            if (Runtime::IsNonReturning(function_id)) {
                // TODO(7099): Investigate if we need LoopExit node here.
                Node* control = NewNode(common()->Throw());
                MergeControlToLeaveFunction(control);
            }
        }

        void BytecodeGraphBuilder::VisitCallRuntimeForPair()
        {
            PrepareEagerCheckpoint();
            Runtime::FunctionId functionId = bytecode_iterator().GetRuntimeIdOperand(0);
            interpreter::Register receiver = bytecode_iterator().GetRegisterOperand(1);
            size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
            interpreter::Register first_return = bytecode_iterator().GetRegisterOperand(3);

            // Create node to perform the runtime call.
            const Operator* call = javascript()->CallRuntime(functionId, reg_count);
            Node* return_pair = ProcessCallRuntimeArguments(call, receiver, reg_count);
            environment()->BindRegistersToProjections(first_return, return_pair,
                Environment::kAttachFrameState);
        }

        Node* const* BytecodeGraphBuilder::GetConstructArgumentsFromRegister(
            Node* target, Node* new_target, interpreter::Register first_arg,
            int arg_count)
        {
            // arity is args + callee and new target.
            int arity = arg_count + 2;
            Node** all = local_zone()->NewArray<Node*>(static_cast<size_t>(arity));
            all[0] = target;
            int first_arg_index = first_arg.index();
            for (int i = 0; i < arg_count; ++i) {
                all[1 + i] = environment()->LookupRegister(
                    interpreter::Register(first_arg_index + i));
            }
            all[arity - 1] = new_target;
            return all;
        }

        Node* BytecodeGraphBuilder::ProcessConstructArguments(const Operator* op,
            Node* const* args,
            int arg_count)
        {
            return MakeNode(op, arg_count, args, false);
        }

        void BytecodeGraphBuilder::VisitConstruct()
        {
            PrepareEagerCheckpoint();
            interpreter::Register callee_reg = bytecode_iterator().GetRegisterOperand(0);
            interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1);
            size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
            int const slot_id = bytecode_iterator().GetIndexOperand(3);
            VectorSlotPair feedback = CreateVectorSlotPair(slot_id);

            Node* new_target = environment()->LookupAccumulator();
            Node* callee = environment()->LookupRegister(callee_reg);

            CallFrequency frequency = ComputeCallFrequency(slot_id);
            const Operator* op = javascript()->Construct(
                static_cast<uint32_t>(reg_count + 2), frequency, feedback);
            int arg_count = static_cast<int>(reg_count);
            Node* const* args = GetConstructArgumentsFromRegister(callee, new_target,
                first_reg, arg_count);
            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedConstruct(
                op, args, static_cast<int>(arg_count), feedback.slot());
            if (lowering.IsExit())
                return;

            Node* node = nullptr;
            if (lowering.IsSideEffectFree()) {
                node = lowering.value();
            } else {
                DCHECK(!lowering.Changed());
                node = ProcessConstructArguments(op, args, 2 + arg_count);
            }
            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitConstructWithSpread()
        {
            PrepareEagerCheckpoint();
            interpreter::Register callee_reg = bytecode_iterator().GetRegisterOperand(0);
            interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1);
            size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);
            int const slot_id = bytecode_iterator().GetIndexOperand(3);
            VectorSlotPair feedback = CreateVectorSlotPair(slot_id);

            Node* new_target = environment()->LookupAccumulator();
            Node* callee = environment()->LookupRegister(callee_reg);

            CallFrequency frequency = ComputeCallFrequency(slot_id);
            const Operator* op = javascript()->ConstructWithSpread(
                static_cast<uint32_t>(reg_count + 2), frequency, feedback);
            int arg_count = static_cast<int>(reg_count);
            Node* const* args = GetConstructArgumentsFromRegister(callee, new_target,
                first_reg, arg_count);
            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedConstruct(
                op, args, static_cast<int>(arg_count), feedback.slot());
            if (lowering.IsExit())
                return;

            Node* node = nullptr;
            if (lowering.IsSideEffectFree()) {
                node = lowering.value();
            } else {
                DCHECK(!lowering.Changed());
                node = ProcessConstructArguments(op, args, 2 + arg_count);
            }
            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitInvokeIntrinsic()
        {
            PrepareEagerCheckpoint();
            Runtime::FunctionId functionId = bytecode_iterator().GetIntrinsicIdOperand(0);
            interpreter::Register receiver = bytecode_iterator().GetRegisterOperand(1);
            size_t reg_count = bytecode_iterator().GetRegisterCountOperand(2);

            // Create node to perform the runtime call. Turbofan will take care of the
            // lowering.
            const Operator* call = javascript()->CallRuntime(functionId, reg_count);
            Node* value = ProcessCallRuntimeArguments(call, receiver, reg_count);
            environment()->BindAccumulator(value, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitThrow()
        {
            BuildLoopExitsForFunctionExit(bytecode_analysis()->GetInLivenessFor(
                bytecode_iterator().current_offset()));
            Node* value = environment()->LookupAccumulator();
            Node* call = NewNode(javascript()->CallRuntime(Runtime::kThrow), value);
            environment()->BindAccumulator(call, Environment::kAttachFrameState);
            Node* control = NewNode(common()->Throw());
            MergeControlToLeaveFunction(control);
        }

        void BytecodeGraphBuilder::VisitAbort()
        {
            BuildLoopExitsForFunctionExit(bytecode_analysis()->GetInLivenessFor(
                bytecode_iterator().current_offset()));
            AbortReason reason = static_cast<AbortReason>(bytecode_iterator().GetIndexOperand(0));
            NewNode(simplified()->RuntimeAbort(reason));
            Node* control = NewNode(common()->Throw());
            MergeControlToLeaveFunction(control);
        }

        void BytecodeGraphBuilder::VisitReThrow()
        {
            BuildLoopExitsForFunctionExit(bytecode_analysis()->GetInLivenessFor(
                bytecode_iterator().current_offset()));
            Node* value = environment()->LookupAccumulator();
            NewNode(javascript()->CallRuntime(Runtime::kReThrow), value);
            Node* control = NewNode(common()->Throw());
            MergeControlToLeaveFunction(control);
        }

        void BytecodeGraphBuilder::BuildHoleCheckAndThrow(
            Node* condition, Runtime::FunctionId runtime_id, Node* name)
        {
            Node* accumulator = environment()->LookupAccumulator();
            NewBranch(condition, BranchHint::kFalse);
            {
                SubEnvironment sub_environment(this);

                NewIfTrue();
                BuildLoopExitsForFunctionExit(bytecode_analysis()->GetInLivenessFor(
                    bytecode_iterator().current_offset()));
                Node* node;
                const Operator* op = javascript()->CallRuntime(runtime_id);
                if (runtime_id == Runtime::kThrowAccessedUninitializedVariable) {
                    DCHECK_NOT_NULL(name);
                    node = NewNode(op, name);
                } else {
                    DCHECK(runtime_id == Runtime::kThrowSuperAlreadyCalledError || runtime_id == Runtime::kThrowSuperNotCalled);
                    node = NewNode(op);
                }
                environment()->RecordAfterState(node, Environment::kAttachFrameState);
                Node* control = NewNode(common()->Throw());
                MergeControlToLeaveFunction(control);
            }
            NewIfFalse();
            environment()->BindAccumulator(accumulator);
        }

        void BytecodeGraphBuilder::VisitThrowReferenceErrorIfHole()
        {
            Node* accumulator = environment()->LookupAccumulator();
            Node* check_for_hole = NewNode(simplified()->ReferenceEqual(), accumulator,
                jsgraph()->TheHoleConstant());
            Node* name = jsgraph()->Constant(
                handle(bytecode_iterator().GetConstantForIndexOperand(0), isolate()));
            BuildHoleCheckAndThrow(check_for_hole,
                Runtime::kThrowAccessedUninitializedVariable, name);
        }

        void BytecodeGraphBuilder::VisitThrowSuperNotCalledIfHole()
        {
            Node* accumulator = environment()->LookupAccumulator();
            Node* check_for_hole = NewNode(simplified()->ReferenceEqual(), accumulator,
                jsgraph()->TheHoleConstant());
            BuildHoleCheckAndThrow(check_for_hole, Runtime::kThrowSuperNotCalled);
        }

        void BytecodeGraphBuilder::VisitThrowSuperAlreadyCalledIfNotHole()
        {
            Node* accumulator = environment()->LookupAccumulator();
            Node* check_for_hole = NewNode(simplified()->ReferenceEqual(), accumulator,
                jsgraph()->TheHoleConstant());
            Node* check_for_not_hole = NewNode(simplified()->BooleanNot(), check_for_hole);
            BuildHoleCheckAndThrow(check_for_not_hole,
                Runtime::kThrowSuperAlreadyCalledError);
        }

        void BytecodeGraphBuilder::BuildUnaryOp(const Operator* op)
        {
            PrepareEagerCheckpoint();
            Node* operand = environment()->LookupAccumulator();

            FeedbackSlot slot = bytecode_iterator().GetSlotOperand(kUnaryOperationHintIndex);
            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedUnaryOp(op, operand, slot);
            if (lowering.IsExit())
                return;

            Node* node = nullptr;
            if (lowering.IsSideEffectFree()) {
                node = lowering.value();
            } else {
                DCHECK(!lowering.Changed());
                node = NewNode(op, operand);
            }

            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::BuildBinaryOp(const Operator* op)
        {
            PrepareEagerCheckpoint();
            Node* left = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Node* right = environment()->LookupAccumulator();

            FeedbackSlot slot = bytecode_iterator().GetSlotOperand(kBinaryOperationHintIndex);
            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedBinaryOp(op, left, right, slot);
            if (lowering.IsExit())
                return;

            Node* node = nullptr;
            if (lowering.IsSideEffectFree()) {
                node = lowering.value();
            } else {
                DCHECK(!lowering.Changed());
                node = NewNode(op, left, right);
            }

            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        // Helper function to create binary operation hint from the recorded type
        // feedback.
        BinaryOperationHint BytecodeGraphBuilder::GetBinaryOperationHint(
            int operand_index)
        {
            FeedbackSlot slot = bytecode_iterator().GetSlotOperand(operand_index);
            FeedbackNexus nexus(feedback_vector(), slot);
            return nexus.GetBinaryOperationFeedback();
        }

        // Helper function to create compare operation hint from the recorded type
        // feedback.
        CompareOperationHint BytecodeGraphBuilder::GetCompareOperationHint()
        {
            FeedbackSlot slot = bytecode_iterator().GetSlotOperand(1);
            FeedbackNexus nexus(feedback_vector(), slot);
            return nexus.GetCompareOperationFeedback();
        }

        // Helper function to create for-in mode from the recorded type feedback.
        ForInMode BytecodeGraphBuilder::GetForInMode(int operand_index)
        {
            FeedbackSlot slot = bytecode_iterator().GetSlotOperand(operand_index);
            FeedbackNexus nexus(feedback_vector(), slot);
            switch (nexus.GetForInFeedback()) {
            case ForInHint::kNone:
            case ForInHint::kEnumCacheKeysAndIndices:
                return ForInMode::kUseEnumCacheKeysAndIndices;
            case ForInHint::kEnumCacheKeys:
                return ForInMode::kUseEnumCacheKeys;
            case ForInHint::kAny:
                return ForInMode::kGeneric;
            }
            UNREACHABLE();
        }

        CallFrequency BytecodeGraphBuilder::ComputeCallFrequency(int slot_id) const
        {
            if (invocation_frequency_.IsUnknown())
                return CallFrequency();
            FeedbackNexus nexus(feedback_vector(), FeedbackVector::ToSlot(slot_id));
            float feedback_frequency = nexus.ComputeCallFrequency();
            if (feedback_frequency == 0.0f) {
                // This is to prevent multiplying zero and infinity.
                return CallFrequency(0.0f);
            } else {
                return CallFrequency(feedback_frequency * invocation_frequency_.value());
            }
        }

        SpeculationMode BytecodeGraphBuilder::GetSpeculationMode(int slot_id) const
        {
            FeedbackNexus nexus(feedback_vector(), FeedbackVector::ToSlot(slot_id));
            return nexus.GetSpeculationMode();
        }

        void BytecodeGraphBuilder::VisitBitwiseNot()
        {
            BuildUnaryOp(javascript()->BitwiseNot());
        }

        void BytecodeGraphBuilder::VisitDec()
        {
            BuildUnaryOp(javascript()->Decrement());
        }

        void BytecodeGraphBuilder::VisitInc()
        {
            BuildUnaryOp(javascript()->Increment());
        }

        void BytecodeGraphBuilder::VisitNegate()
        {
            BuildUnaryOp(javascript()->Negate());
        }

        void BytecodeGraphBuilder::VisitAdd()
        {
            BuildBinaryOp(
                javascript()->Add(GetBinaryOperationHint(kBinaryOperationHintIndex)));
        }

        void BytecodeGraphBuilder::VisitSub()
        {
            BuildBinaryOp(javascript()->Subtract());
        }

        void BytecodeGraphBuilder::VisitMul()
        {
            BuildBinaryOp(javascript()->Multiply());
        }

        void BytecodeGraphBuilder::VisitDiv() { BuildBinaryOp(javascript()->Divide()); }

        void BytecodeGraphBuilder::VisitMod()
        {
            BuildBinaryOp(javascript()->Modulus());
        }

        void BytecodeGraphBuilder::VisitExp()
        {
            BuildBinaryOp(javascript()->Exponentiate());
        }

        void BytecodeGraphBuilder::VisitBitwiseOr()
        {
            BuildBinaryOp(javascript()->BitwiseOr());
        }

        void BytecodeGraphBuilder::VisitBitwiseXor()
        {
            BuildBinaryOp(javascript()->BitwiseXor());
        }

        void BytecodeGraphBuilder::VisitBitwiseAnd()
        {
            BuildBinaryOp(javascript()->BitwiseAnd());
        }

        void BytecodeGraphBuilder::VisitShiftLeft()
        {
            BuildBinaryOp(javascript()->ShiftLeft());
        }

        void BytecodeGraphBuilder::VisitShiftRight()
        {
            BuildBinaryOp(javascript()->ShiftRight());
        }

        void BytecodeGraphBuilder::VisitShiftRightLogical()
        {
            BuildBinaryOp(javascript()->ShiftRightLogical());
        }

        void BytecodeGraphBuilder::BuildBinaryOpWithImmediate(const Operator* op)
        {
            PrepareEagerCheckpoint();
            Node* left = environment()->LookupAccumulator();
            Node* right = jsgraph()->Constant(bytecode_iterator().GetImmediateOperand(0));

            FeedbackSlot slot = bytecode_iterator().GetSlotOperand(kBinaryOperationSmiHintIndex);
            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedBinaryOp(op, left, right, slot);
            if (lowering.IsExit())
                return;

            Node* node = nullptr;
            if (lowering.IsSideEffectFree()) {
                node = lowering.value();
            } else {
                DCHECK(!lowering.Changed());
                node = NewNode(op, left, right);
            }
            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitAddSmi()
        {
            BuildBinaryOpWithImmediate(
                javascript()->Add(GetBinaryOperationHint(kBinaryOperationSmiHintIndex)));
        }

        void BytecodeGraphBuilder::VisitSubSmi()
        {
            BuildBinaryOpWithImmediate(javascript()->Subtract());
        }

        void BytecodeGraphBuilder::VisitMulSmi()
        {
            BuildBinaryOpWithImmediate(javascript()->Multiply());
        }

        void BytecodeGraphBuilder::VisitDivSmi()
        {
            BuildBinaryOpWithImmediate(javascript()->Divide());
        }

        void BytecodeGraphBuilder::VisitModSmi()
        {
            BuildBinaryOpWithImmediate(javascript()->Modulus());
        }

        void BytecodeGraphBuilder::VisitExpSmi()
        {
            BuildBinaryOpWithImmediate(javascript()->Exponentiate());
        }

        void BytecodeGraphBuilder::VisitBitwiseOrSmi()
        {
            BuildBinaryOpWithImmediate(javascript()->BitwiseOr());
        }

        void BytecodeGraphBuilder::VisitBitwiseXorSmi()
        {
            BuildBinaryOpWithImmediate(javascript()->BitwiseXor());
        }

        void BytecodeGraphBuilder::VisitBitwiseAndSmi()
        {
            BuildBinaryOpWithImmediate(javascript()->BitwiseAnd());
        }

        void BytecodeGraphBuilder::VisitShiftLeftSmi()
        {
            BuildBinaryOpWithImmediate(javascript()->ShiftLeft());
        }

        void BytecodeGraphBuilder::VisitShiftRightSmi()
        {
            BuildBinaryOpWithImmediate(javascript()->ShiftRight());
        }

        void BytecodeGraphBuilder::VisitShiftRightLogicalSmi()
        {
            BuildBinaryOpWithImmediate(javascript()->ShiftRightLogical());
        }

        void BytecodeGraphBuilder::VisitLogicalNot()
        {
            Node* value = environment()->LookupAccumulator();
            Node* node = NewNode(simplified()->BooleanNot(), value);
            environment()->BindAccumulator(node);
        }

        void BytecodeGraphBuilder::VisitToBooleanLogicalNot()
        {
            Node* value = NewNode(simplified()->ToBoolean(), environment()->LookupAccumulator());
            Node* node = NewNode(simplified()->BooleanNot(), value);
            environment()->BindAccumulator(node);
        }

        void BytecodeGraphBuilder::VisitTypeOf()
        {
            Node* node = NewNode(simplified()->TypeOf(), environment()->LookupAccumulator());
            environment()->BindAccumulator(node);
        }

        void BytecodeGraphBuilder::BuildDelete(LanguageMode language_mode)
        {
            PrepareEagerCheckpoint();
            Node* key = environment()->LookupAccumulator();
            Node* object = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Node* mode = jsgraph()->Constant(static_cast<int32_t>(language_mode));
            Node* node = NewNode(javascript()->DeleteProperty(), object, key, mode);
            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitDeletePropertyStrict()
        {
            BuildDelete(LanguageMode::kStrict);
        }

        void BytecodeGraphBuilder::VisitDeletePropertySloppy()
        {
            BuildDelete(LanguageMode::kSloppy);
        }

        void BytecodeGraphBuilder::VisitGetSuperConstructor()
        {
            Node* node = NewNode(javascript()->GetSuperConstructor(),
                environment()->LookupAccumulator());
            environment()->BindRegister(bytecode_iterator().GetRegisterOperand(0), node,
                Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::BuildCompareOp(const Operator* op)
        {
            PrepareEagerCheckpoint();
            Node* left = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Node* right = environment()->LookupAccumulator();

            FeedbackSlot slot = bytecode_iterator().GetSlotOperand(1);
            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedBinaryOp(op, left, right, slot);
            if (lowering.IsExit())
                return;

            Node* node = nullptr;
            if (lowering.IsSideEffectFree()) {
                node = lowering.value();
            } else {
                DCHECK(!lowering.Changed());
                node = NewNode(op, left, right);
            }
            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitTestEqual()
        {
            BuildCompareOp(javascript()->Equal(GetCompareOperationHint()));
        }

        void BytecodeGraphBuilder::VisitTestEqualStrict()
        {
            BuildCompareOp(javascript()->StrictEqual(GetCompareOperationHint()));
        }

        void BytecodeGraphBuilder::VisitTestLessThan()
        {
            BuildCompareOp(javascript()->LessThan(GetCompareOperationHint()));
        }

        void BytecodeGraphBuilder::VisitTestGreaterThan()
        {
            BuildCompareOp(javascript()->GreaterThan(GetCompareOperationHint()));
        }

        void BytecodeGraphBuilder::VisitTestLessThanOrEqual()
        {
            BuildCompareOp(javascript()->LessThanOrEqual(GetCompareOperationHint()));
        }

        void BytecodeGraphBuilder::VisitTestGreaterThanOrEqual()
        {
            BuildCompareOp(javascript()->GreaterThanOrEqual(GetCompareOperationHint()));
        }

        void BytecodeGraphBuilder::VisitTestReferenceEqual()
        {
            Node* left = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Node* right = environment()->LookupAccumulator();
            Node* result = NewNode(simplified()->ReferenceEqual(), left, right);
            environment()->BindAccumulator(result);
        }

        void BytecodeGraphBuilder::VisitTestIn()
        {
            PrepareEagerCheckpoint();
            Node* object = environment()->LookupAccumulator();
            Node* key = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            VectorSlotPair feedback = CreateVectorSlotPair(bytecode_iterator().GetIndexOperand(1));
            Node* node = NewNode(javascript()->HasProperty(feedback), object, key);
            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitTestInstanceOf()
        {
            int const slot_index = bytecode_iterator().GetIndexOperand(1);
            BuildCompareOp(javascript()->InstanceOf(CreateVectorSlotPair(slot_index)));
        }

        void BytecodeGraphBuilder::VisitTestUndetectable()
        {
            Node* object = environment()->LookupAccumulator();
            Node* node = NewNode(jsgraph()->simplified()->ObjectIsUndetectable(), object);
            environment()->BindAccumulator(node);
        }

        void BytecodeGraphBuilder::VisitTestNull()
        {
            Node* object = environment()->LookupAccumulator();
            Node* result = NewNode(simplified()->ReferenceEqual(), object,
                jsgraph()->NullConstant());
            environment()->BindAccumulator(result);
        }

        void BytecodeGraphBuilder::VisitTestUndefined()
        {
            Node* object = environment()->LookupAccumulator();
            Node* result = NewNode(simplified()->ReferenceEqual(), object,
                jsgraph()->UndefinedConstant());
            environment()->BindAccumulator(result);
        }

        void BytecodeGraphBuilder::VisitTestTypeOf()
        {
            Node* object = environment()->LookupAccumulator();
            auto literal_flag = interpreter::TestTypeOfFlags::Decode(
                bytecode_iterator().GetFlagOperand(0));
            Node* result = nullptr;
            switch (literal_flag) {
            case interpreter::TestTypeOfFlags::LiteralFlag::kNumber:
                result = NewNode(simplified()->ObjectIsNumber(), object);
                break;
            case interpreter::TestTypeOfFlags::LiteralFlag::kString:
                result = NewNode(simplified()->ObjectIsString(), object);
                break;
            case interpreter::TestTypeOfFlags::LiteralFlag::kSymbol:
                result = NewNode(simplified()->ObjectIsSymbol(), object);
                break;
            case interpreter::TestTypeOfFlags::LiteralFlag::kBigInt:
                result = NewNode(simplified()->ObjectIsBigInt(), object);
                break;
            case interpreter::TestTypeOfFlags::LiteralFlag::kBoolean:
                result = NewNode(common()->Select(MachineRepresentation::kTagged),
                    NewNode(simplified()->ReferenceEqual(), object,
                        jsgraph()->TrueConstant()),
                    jsgraph()->TrueConstant(),
                    NewNode(simplified()->ReferenceEqual(), object,
                        jsgraph()->FalseConstant()));
                break;
            case interpreter::TestTypeOfFlags::LiteralFlag::kUndefined:
                result = graph()->NewNode(
                    common()->Select(MachineRepresentation::kTagged),
                    graph()->NewNode(simplified()->ReferenceEqual(), object,
                        jsgraph()->NullConstant()),
                    jsgraph()->FalseConstant(),
                    graph()->NewNode(simplified()->ObjectIsUndetectable(), object));
                break;
            case interpreter::TestTypeOfFlags::LiteralFlag::kFunction:
                result = graph()->NewNode(simplified()->ObjectIsDetectableCallable(), object);
                break;
            case interpreter::TestTypeOfFlags::LiteralFlag::kObject:
                result = graph()->NewNode(
                    common()->Select(MachineRepresentation::kTagged),
                    graph()->NewNode(simplified()->ObjectIsNonCallable(), object),
                    jsgraph()->TrueConstant(),
                    graph()->NewNode(simplified()->ReferenceEqual(), object,
                        jsgraph()->NullConstant()));
                break;
            case interpreter::TestTypeOfFlags::LiteralFlag::kOther:
                UNREACHABLE(); // Should never be emitted.
                break;
            }
            environment()->BindAccumulator(result);
        }

        void BytecodeGraphBuilder::BuildCastOperator(const Operator* js_op)
        {
            Node* value = NewNode(js_op, environment()->LookupAccumulator());
            environment()->BindRegister(bytecode_iterator().GetRegisterOperand(0), value,
                Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitToName()
        {
            BuildCastOperator(javascript()->ToName());
        }

        void BytecodeGraphBuilder::VisitToObject()
        {
            BuildCastOperator(javascript()->ToObject());
        }

        void BytecodeGraphBuilder::VisitToString()
        {
            Node* value = NewNode(javascript()->ToString(), environment()->LookupAccumulator());
            environment()->BindAccumulator(value, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitToNumber()
        {
            PrepareEagerCheckpoint();
            Node* object = environment()->LookupAccumulator();

            FeedbackSlot slot = bytecode_iterator().GetSlotOperand(0);
            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedToNumber(object, slot);

            Node* node = nullptr;
            if (lowering.IsSideEffectFree()) {
                node = lowering.value();
            } else {
                DCHECK(!lowering.Changed());
                node = NewNode(javascript()->ToNumber(), object);
            }

            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitToNumeric()
        {
            PrepareEagerCheckpoint();
            Node* object = environment()->LookupAccumulator();

            // If we have some kind of Number feedback, we do the same lowering as for
            // ToNumber.
            FeedbackSlot slot = bytecode_iterator().GetSlotOperand(0);
            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedToNumber(object, slot);

            Node* node = nullptr;
            if (lowering.IsSideEffectFree()) {
                node = lowering.value();
            } else {
                DCHECK(!lowering.Changed());
                node = NewNode(javascript()->ToNumeric(), object);
            }

            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitJump() { BuildJump(); }

        void BytecodeGraphBuilder::VisitJumpConstant() { BuildJump(); }

        void BytecodeGraphBuilder::VisitJumpIfTrue() { BuildJumpIfTrue(); }

        void BytecodeGraphBuilder::VisitJumpIfTrueConstant() { BuildJumpIfTrue(); }

        void BytecodeGraphBuilder::VisitJumpIfFalse() { BuildJumpIfFalse(); }

        void BytecodeGraphBuilder::VisitJumpIfFalseConstant() { BuildJumpIfFalse(); }

        void BytecodeGraphBuilder::VisitJumpIfToBooleanTrue()
        {
            BuildJumpIfToBooleanTrue();
        }

        void BytecodeGraphBuilder::VisitJumpIfToBooleanTrueConstant()
        {
            BuildJumpIfToBooleanTrue();
        }

        void BytecodeGraphBuilder::VisitJumpIfToBooleanFalse()
        {
            BuildJumpIfToBooleanFalse();
        }

        void BytecodeGraphBuilder::VisitJumpIfToBooleanFalseConstant()
        {
            BuildJumpIfToBooleanFalse();
        }

        void BytecodeGraphBuilder::VisitJumpIfJSReceiver() { BuildJumpIfJSReceiver(); }

        void BytecodeGraphBuilder::VisitJumpIfJSReceiverConstant()
        {
            BuildJumpIfJSReceiver();
        }

        void BytecodeGraphBuilder::VisitJumpIfNull()
        {
            BuildJumpIfEqual(jsgraph()->NullConstant());
        }

        void BytecodeGraphBuilder::VisitJumpIfNullConstant()
        {
            BuildJumpIfEqual(jsgraph()->NullConstant());
        }

        void BytecodeGraphBuilder::VisitJumpIfNotNull()
        {
            BuildJumpIfNotEqual(jsgraph()->NullConstant());
        }

        void BytecodeGraphBuilder::VisitJumpIfNotNullConstant()
        {
            BuildJumpIfNotEqual(jsgraph()->NullConstant());
        }

        void BytecodeGraphBuilder::VisitJumpIfUndefined()
        {
            BuildJumpIfEqual(jsgraph()->UndefinedConstant());
        }

        void BytecodeGraphBuilder::VisitJumpIfUndefinedConstant()
        {
            BuildJumpIfEqual(jsgraph()->UndefinedConstant());
        }

        void BytecodeGraphBuilder::VisitJumpIfNotUndefined()
        {
            BuildJumpIfNotEqual(jsgraph()->UndefinedConstant());
        }

        void BytecodeGraphBuilder::VisitJumpIfNotUndefinedConstant()
        {
            BuildJumpIfNotEqual(jsgraph()->UndefinedConstant());
        }

        void BytecodeGraphBuilder::VisitJumpLoop() { BuildJump(); }

        void BytecodeGraphBuilder::BuildSwitchOnSmi(Node* condition)
        {
            interpreter::JumpTableTargetOffsets offsets = bytecode_iterator().GetJumpTableTargetOffsets();

            NewSwitch(condition, offsets.size() + 1);
            for (const auto& entry : offsets) {
                SubEnvironment sub_environment(this);
                NewIfValue(entry.case_value);
                MergeIntoSuccessorEnvironment(entry.target_offset);
            }
            NewIfDefault();
        }

        void BytecodeGraphBuilder::VisitSwitchOnSmiNoFeedback()
        {
            PrepareEagerCheckpoint();

            Node* acc = environment()->LookupAccumulator();
            Node* acc_smi = NewNode(simplified()->CheckSmi(VectorSlotPair()), acc);
            BuildSwitchOnSmi(acc_smi);
        }

        void BytecodeGraphBuilder::VisitStackCheck()
        {
            PrepareEagerCheckpoint();
            Node* node = NewNode(javascript()->StackCheck());
            environment()->RecordAfterState(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitSetPendingMessage()
        {
            Node* previous_message = NewNode(javascript()->LoadMessage());
            NewNode(javascript()->StoreMessage(), environment()->LookupAccumulator());
            environment()->BindAccumulator(previous_message);
        }

        void BytecodeGraphBuilder::BuildReturn(const BytecodeLivenessState* liveness)
        {
            BuildLoopExitsForFunctionExit(liveness);
            Node* pop_node = jsgraph()->ZeroConstant();
            Node* control = NewNode(common()->Return(), pop_node, environment()->LookupAccumulator());
            MergeControlToLeaveFunction(control);
        }

        void BytecodeGraphBuilder::VisitReturn()
        {
            BuildReturn(bytecode_analysis()->GetInLivenessFor(
                bytecode_iterator().current_offset()));
        }

        void BytecodeGraphBuilder::VisitDebugger()
        {
            PrepareEagerCheckpoint();
            Node* call = NewNode(javascript()->Debugger());
            environment()->RecordAfterState(call, Environment::kAttachFrameState);
        }

// We cannot create a graph from the debugger copy of the bytecode array.
#define DEBUG_BREAK(Name, ...) \
    void BytecodeGraphBuilder::Visit##Name() { UNREACHABLE(); }
        DEBUG_BREAK_BYTECODE_LIST(DEBUG_BREAK)
#undef DEBUG_BREAK

        void BytecodeGraphBuilder::VisitIncBlockCounter()
        {
            Node* closure = GetFunctionClosure();
            Node* coverage_array_slot = jsgraph()->Constant(bytecode_iterator().GetIndexOperand(0));

            const Operator* op = javascript()->CallRuntime(Runtime::kIncBlockCounter);

            NewNode(op, closure, coverage_array_slot);
        }

        void BytecodeGraphBuilder::VisitForInEnumerate()
        {
            Node* receiver = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Node* enumerator = NewNode(javascript()->ForInEnumerate(), receiver);
            environment()->BindAccumulator(enumerator, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitForInPrepare()
        {
            PrepareEagerCheckpoint();
            Node* enumerator = environment()->LookupAccumulator();

            FeedbackSlot slot = bytecode_iterator().GetSlotOperand(1);
            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedForInPrepare(enumerator, slot);
            if (lowering.IsExit())
                return;
            DCHECK(!lowering.Changed());
            Node* node = NewNode(javascript()->ForInPrepare(GetForInMode(1)), enumerator);
            environment()->BindRegistersToProjections(
                bytecode_iterator().GetRegisterOperand(0), node);
        }

        void BytecodeGraphBuilder::VisitForInContinue()
        {
            PrepareEagerCheckpoint();
            Node* index = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Node* cache_length = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
            Node* exit_cond = NewNode(simplified()->SpeculativeNumberLessThan(
                                          NumberOperationHint::kSignedSmall),
                index, cache_length);
            environment()->BindAccumulator(exit_cond);
        }

        void BytecodeGraphBuilder::VisitForInNext()
        {
            PrepareEagerCheckpoint();
            Node* receiver = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            Node* index = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
            int catch_reg_pair_index = bytecode_iterator().GetRegisterOperand(2).index();
            Node* cache_type = environment()->LookupRegister(
                interpreter::Register(catch_reg_pair_index));
            Node* cache_array = environment()->LookupRegister(
                interpreter::Register(catch_reg_pair_index + 1));

            // We need to rename the {index} here, as in case of OSR we loose the
            // information that the {index} is always a valid unsigned Smi value.
            index = graph()->NewNode(common()->TypeGuard(Type::UnsignedSmall()), index,
                environment()->GetEffectDependency(),
                environment()->GetControlDependency());
            environment()->UpdateEffectDependency(index);

            FeedbackSlot slot = bytecode_iterator().GetSlotOperand(3);
            JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedForInNext(
                receiver, cache_array, cache_type, index, slot);
            if (lowering.IsExit())
                return;

            DCHECK(!lowering.Changed());
            Node* node = NewNode(javascript()->ForInNext(GetForInMode(3)), receiver,
                cache_array, cache_type, index);
            environment()->BindAccumulator(node, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitForInStep()
        {
            PrepareEagerCheckpoint();
            Node* index = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            index = NewNode(simplified()->SpeculativeSafeIntegerAdd(
                                NumberOperationHint::kSignedSmall),
                index, jsgraph()->OneConstant());
            environment()->BindAccumulator(index, Environment::kAttachFrameState);
        }

        void BytecodeGraphBuilder::VisitSuspendGenerator()
        {
            Node* generator = environment()->LookupRegister(
                bytecode_iterator().GetRegisterOperand(0));
            interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1);
            // We assume we are storing a range starting from index 0.
            CHECK_EQ(0, first_reg.index());
            int register_count = static_cast<int>(bytecode_iterator().GetRegisterCountOperand(2));
            int parameter_count_without_receiver = bytecode_array()->parameter_count() - 1;

            Node* suspend_id = jsgraph()->SmiConstant(
                bytecode_iterator().GetUnsignedImmediateOperand(3));

            // The offsets used by the bytecode iterator are relative to a different base
            // than what is used in the interpreter, hence the addition.
            Node* offset = jsgraph()->Constant(bytecode_iterator().current_offset() + (BytecodeArray::kHeaderSize - kHeapObjectTag));

            const BytecodeLivenessState* liveness = bytecode_analysis()->GetInLivenessFor(
                bytecode_iterator().current_offset());

            // Maybe overallocate the value list since we don't know how many registers
            // are live.
            // TODO(leszeks): We could get this count from liveness rather than the
            // register list.
            int value_input_count = 3 + parameter_count_without_receiver + register_count;

            Node** value_inputs = local_zone()->NewArray<Node*>(value_input_count);
            value_inputs[0] = generator;
            value_inputs[1] = suspend_id;
            value_inputs[2] = offset;

            int count_written = 0;
            // Store the parameters.
            for (int i = 0; i < parameter_count_without_receiver; i++) {
                value_inputs[3 + count_written++] = environment()->LookupRegister(interpreter::Register::FromParameterIndex(
                    i, parameter_count_without_receiver));
            }

            // Store the registers.
            for (int i = 0; i < register_count; ++i) {
                if (liveness == nullptr || liveness->RegisterIsLive(i)) {
                    int index_in_parameters_and_registers = parameter_count_without_receiver + i;
                    while (count_written < index_in_parameters_and_registers) {
                        value_inputs[3 + count_written++] = jsgraph()->OptimizedOutConstant();
                    }
                    value_inputs[3 + count_written++] = environment()->LookupRegister(interpreter::Register(i));
                    DCHECK_EQ(count_written, index_in_parameters_and_registers + 1);
                }
            }

            // Use the actual written count rather than the register count to create the
            // node.
            MakeNode(javascript()->GeneratorStore(count_written), 3 + count_written,
                value_inputs, false);

            // TODO(leszeks): This over-approximates the liveness at exit, only the
            // accumulator should be live by this point.
            BuildReturn(bytecode_analysis()->GetInLivenessFor(
                bytecode_iterator().current_offset()));
        }

        void BytecodeGraphBuilder::BuildSwitchOnGeneratorState(
            const ZoneVector<ResumeJumpTarget>& resume_jump_targets,
            bool allow_fallthrough_on_executing)
        {
            Node* generator_state = environment()->LookupGeneratorState();

            int extra_cases = allow_fallthrough_on_executing ? 2 : 1;
            NewSwitch(generator_state,
                static_cast<int>(resume_jump_targets.size() + extra_cases));
            for (const ResumeJumpTarget& target : resume_jump_targets) {
                SubEnvironment sub_environment(this);
                NewIfValue(target.suspend_id());
                if (target.is_leaf()) {
                    // Mark that we are resuming executing.
                    environment()->BindGeneratorState(
                        jsgraph()->SmiConstant(JSGeneratorObject::kGeneratorExecuting));
                }
                // Jump to the target offset, whether it's a loop header or the resume.
                MergeIntoSuccessorEnvironment(target.target_offset());
            }

            {
                SubEnvironment sub_environment(this);
                // We should never hit the default case (assuming generator state cannot be
                // corrupted), so abort if we do.
                // TODO(leszeks): Maybe only check this in debug mode, and otherwise use
                // the default to represent one of the cases above/fallthrough below?
                NewIfDefault();
                NewNode(simplified()->RuntimeAbort(AbortReason::kInvalidJumpTableIndex));
                // TODO(7099): Investigate if we need LoopExit here.
                Node* control = NewNode(common()->Throw());
                MergeControlToLeaveFunction(control);
            }

            if (allow_fallthrough_on_executing) {
                // If we are executing (rather than resuming), and we allow it, just fall
                // through to the actual loop body.
                NewIfValue(JSGeneratorObject::kGeneratorExecuting);
            } else {
                // Otherwise, this environment is dead.
                set_environment(nullptr);
            }
        }

        void BytecodeGraphBuilder::VisitSwitchOnGeneratorState()
        {
            Node* generator = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));

            Node* generator_is_undefined = NewNode(simplified()->ReferenceEqual(), generator,
                jsgraph()->UndefinedConstant());

            NewBranch(generator_is_undefined);
            {
                SubEnvironment resume_env(this);
                NewIfFalse();

                Node* generator_state = NewNode(javascript()->GeneratorRestoreContinuation(), generator);
                environment()->BindGeneratorState(generator_state);

                Node* generator_context = NewNode(javascript()->GeneratorRestoreContext(), generator);
                environment()->SetContext(generator_context);

                BuildSwitchOnGeneratorState(bytecode_analysis()->resume_jump_targets(),
                    false);
            }

            // Fallthrough for the first-call case.
            NewIfTrue();
        }

        void BytecodeGraphBuilder::VisitResumeGenerator()
        {
            Node* generator = environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
            interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(1);
            // We assume we are restoring registers starting fromm index 0.
            CHECK_EQ(0, first_reg.index());

            const BytecodeLivenessState* liveness = bytecode_analysis()->GetOutLivenessFor(
                bytecode_iterator().current_offset());

            int parameter_count_without_receiver = bytecode_array()->parameter_count() - 1;

            // Mapping between registers and array indices must match that used in
            // InterpreterAssembler::ExportParametersAndRegisterFile.
            for (int i = 0; i < environment()->register_count(); ++i) {
                if (liveness == nullptr || liveness->RegisterIsLive(i)) {
                    Node* value = NewNode(javascript()->GeneratorRestoreRegister(
                                              parameter_count_without_receiver + i),
                        generator);
                    environment()->BindRegister(interpreter::Register(i), value);
                }
            }

            // Update the accumulator with the generator's input_or_debug_pos.
            Node* input_or_debug_pos = NewNode(javascript()->GeneratorRestoreInputOrDebugPos(), generator);
            environment()->BindAccumulator(input_or_debug_pos);
        }

        void BytecodeGraphBuilder::VisitWide()
        {
            // Consumed by the BytecodeArrayIterator.
            UNREACHABLE();
        }

        void BytecodeGraphBuilder::VisitExtraWide()
        {
            // Consumed by the BytecodeArrayIterator.
            UNREACHABLE();
        }

        void BytecodeGraphBuilder::VisitIllegal()
        {
            // Not emitted in valid bytecode.
            UNREACHABLE();
        }

        void BytecodeGraphBuilder::SwitchToMergeEnvironment(int current_offset)
        {
            auto it = merge_environments_.find(current_offset);
            if (it != merge_environments_.end()) {
                mark_as_needing_eager_checkpoint(true);
                if (environment() != nullptr) {
                    it->second->Merge(environment(),
                        bytecode_analysis()->GetInLivenessFor(current_offset));
                }
                set_environment(it->second);
            }
        }

        void BytecodeGraphBuilder::BuildLoopHeaderEnvironment(int current_offset)
        {
            if (bytecode_analysis()->IsLoopHeader(current_offset)) {
                mark_as_needing_eager_checkpoint(true);
                const LoopInfo& loop_info = bytecode_analysis()->GetLoopInfoFor(current_offset);
                const BytecodeLivenessState* liveness = bytecode_analysis()->GetInLivenessFor(current_offset);

                const auto& resume_jump_targets = loop_info.resume_jump_targets();
                bool generate_suspend_switch = !resume_jump_targets.empty();

                // Add loop header.
                environment()->PrepareForLoop(loop_info.assignments(), liveness);

                // Store a copy of the environment so we can connect merged back edge inputs
                // to the loop header.
                merge_environments_[current_offset] = environment()->Copy();

                // If this loop contains resumes, create a new switch just after the loop
                // for those resumes.
                if (generate_suspend_switch) {
                    BuildSwitchOnGeneratorState(loop_info.resume_jump_targets(), true);

                    // TODO(leszeks): At this point we know we are executing rather than
                    // resuming, so we should be able to prune off the phis in the environment
                    // related to the resume path.

                    // Set the generator state to a known constant.
                    environment()->BindGeneratorState(
                        jsgraph()->SmiConstant(JSGeneratorObject::kGeneratorExecuting));
                }
            }
        }

        void BytecodeGraphBuilder::MergeIntoSuccessorEnvironment(int target_offset)
        {
            BuildLoopExitsForBranch(target_offset);
            Environment*& merge_environment = merge_environments_[target_offset];

            if (merge_environment == nullptr) {
                // Append merge nodes to the environment. We may merge here with another
                // environment. So add a place holder for merge nodes. We may add redundant
                // but will be eliminated in a later pass.
                // TODO(mstarzinger): Be smarter about this!
                NewMerge();
                merge_environment = environment();
            } else {
                // Merge any values which are live coming into the successor.
                merge_environment->Merge(
                    environment(), bytecode_analysis()->GetInLivenessFor(target_offset));
            }
            set_environment(nullptr);
        }

        void BytecodeGraphBuilder::MergeControlToLeaveFunction(Node* exit)
        {
            exit_controls_.push_back(exit);
            set_environment(nullptr);
        }

        void BytecodeGraphBuilder::BuildLoopExitsForBranch(int target_offset)
        {
            int origin_offset = bytecode_iterator().current_offset();
            // Only build loop exits for forward edges.
            if (target_offset > origin_offset) {
                BuildLoopExitsUntilLoop(
                    bytecode_analysis()->GetLoopOffsetFor(target_offset),
                    bytecode_analysis()->GetInLivenessFor(target_offset));
            }
        }

        void BytecodeGraphBuilder::BuildLoopExitsUntilLoop(
            int loop_offset, const BytecodeLivenessState* liveness)
        {
            int origin_offset = bytecode_iterator().current_offset();
            int current_loop = bytecode_analysis()->GetLoopOffsetFor(origin_offset);
            // The limit_offset is the stop offset for building loop exists, used for OSR.
            // It prevents the creations of loopexits for loops which do not exist.
            loop_offset = std::max(loop_offset, currently_peeled_loop_offset_);

            while (loop_offset < current_loop) {
                Node* loop_node = merge_environments_[current_loop]->GetControlDependency();
                const LoopInfo& loop_info = bytecode_analysis()->GetLoopInfoFor(current_loop);
                environment()->PrepareForLoopExit(loop_node, loop_info.assignments(),
                    liveness);
                current_loop = loop_info.parent_offset();
            }
        }

        void BytecodeGraphBuilder::BuildLoopExitsForFunctionExit(
            const BytecodeLivenessState* liveness)
        {
            BuildLoopExitsUntilLoop(-1, liveness);
        }

        void BytecodeGraphBuilder::BuildJump()
        {
            MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset());
        }

        void BytecodeGraphBuilder::BuildJumpIf(Node* condition)
        {
            NewBranch(condition, BranchHint::kNone, IsSafetyCheck::kNoSafetyCheck);
            {
                SubEnvironment sub_environment(this);
                NewIfTrue();
                MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset());
            }
            NewIfFalse();
        }

        void BytecodeGraphBuilder::BuildJumpIfNot(Node* condition)
        {
            NewBranch(condition, BranchHint::kNone, IsSafetyCheck::kNoSafetyCheck);
            {
                SubEnvironment sub_environment(this);
                NewIfFalse();
                MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset());
            }
            NewIfTrue();
        }

        void BytecodeGraphBuilder::BuildJumpIfEqual(Node* comperand)
        {
            Node* accumulator = environment()->LookupAccumulator();
            Node* condition = NewNode(simplified()->ReferenceEqual(), accumulator, comperand);
            BuildJumpIf(condition);
        }

        void BytecodeGraphBuilder::BuildJumpIfNotEqual(Node* comperand)
        {
            Node* accumulator = environment()->LookupAccumulator();
            Node* condition = NewNode(simplified()->ReferenceEqual(), accumulator, comperand);
            BuildJumpIfNot(condition);
        }

        void BytecodeGraphBuilder::BuildJumpIfFalse()
        {
            NewBranch(environment()->LookupAccumulator(), BranchHint::kNone,
                IsSafetyCheck::kNoSafetyCheck);
            {
                SubEnvironment sub_environment(this);
                NewIfFalse();
                environment()->BindAccumulator(jsgraph()->FalseConstant());
                MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset());
            }
            NewIfTrue();
            environment()->BindAccumulator(jsgraph()->TrueConstant());
        }

        void BytecodeGraphBuilder::BuildJumpIfTrue()
        {
            NewBranch(environment()->LookupAccumulator(), BranchHint::kNone,
                IsSafetyCheck::kNoSafetyCheck);
            {
                SubEnvironment sub_environment(this);
                NewIfTrue();
                environment()->BindAccumulator(jsgraph()->TrueConstant());
                MergeIntoSuccessorEnvironment(bytecode_iterator().GetJumpTargetOffset());
            }
            NewIfFalse();
            environment()->BindAccumulator(jsgraph()->FalseConstant());
        }

        void BytecodeGraphBuilder::BuildJumpIfToBooleanTrue()
        {
            Node* accumulator = environment()->LookupAccumulator();
            Node* condition = NewNode(simplified()->ToBoolean(), accumulator);
            BuildJumpIf(condition);
        }

        void BytecodeGraphBuilder::BuildJumpIfToBooleanFalse()
        {
            Node* accumulator = environment()->LookupAccumulator();
            Node* condition = NewNode(simplified()->ToBoolean(), accumulator);
            BuildJumpIfNot(condition);
        }

        void BytecodeGraphBuilder::BuildJumpIfNotHole()
        {
            Node* accumulator = environment()->LookupAccumulator();
            Node* condition = NewNode(simplified()->ReferenceEqual(), accumulator,
                jsgraph()->TheHoleConstant());
            BuildJumpIfNot(condition);
        }

        void BytecodeGraphBuilder::BuildJumpIfJSReceiver()
        {
            Node* accumulator = environment()->LookupAccumulator();
            Node* condition = NewNode(simplified()->ObjectIsReceiver(), accumulator);
            BuildJumpIf(condition);
        }

        JSTypeHintLowering::LoweringResult
        BytecodeGraphBuilder::TryBuildSimplifiedUnaryOp(const Operator* op,
            Node* operand,
            FeedbackSlot slot)
        {
            Node* effect = environment()->GetEffectDependency();
            Node* control = environment()->GetControlDependency();
            JSTypeHintLowering::LoweringResult result = type_hint_lowering().ReduceUnaryOperation(op, operand, effect, control,
                slot);
            ApplyEarlyReduction(result);
            return result;
        }

        JSTypeHintLowering::LoweringResult
        BytecodeGraphBuilder::TryBuildSimplifiedBinaryOp(const Operator* op, Node* left,
            Node* right,
            FeedbackSlot slot)
        {
            Node* effect = environment()->GetEffectDependency();
            Node* control = environment()->GetControlDependency();
            JSTypeHintLowering::LoweringResult result = type_hint_lowering().ReduceBinaryOperation(op, left, right, effect,
                control, slot);
            ApplyEarlyReduction(result);
            return result;
        }

        JSTypeHintLowering::LoweringResult
        BytecodeGraphBuilder::TryBuildSimplifiedForInNext(Node* receiver,
            Node* cache_array,
            Node* cache_type, Node* index,
            FeedbackSlot slot)
        {
            Node* effect = environment()->GetEffectDependency();
            Node* control = environment()->GetControlDependency();
            JSTypeHintLowering::LoweringResult result = type_hint_lowering().ReduceForInNextOperation(
                receiver, cache_array, cache_type, index, effect, control, slot);
            ApplyEarlyReduction(result);
            return result;
        }

        JSTypeHintLowering::LoweringResult
        BytecodeGraphBuilder::TryBuildSimplifiedForInPrepare(Node* enumerator,
            FeedbackSlot slot)
        {
            Node* effect = environment()->GetEffectDependency();
            Node* control = environment()->GetControlDependency();
            JSTypeHintLowering::LoweringResult result = type_hint_lowering().ReduceForInPrepareOperation(enumerator, effect,
                control, slot);
            ApplyEarlyReduction(result);
            return result;
        }

        JSTypeHintLowering::LoweringResult
        BytecodeGraphBuilder::TryBuildSimplifiedToNumber(Node* value,
            FeedbackSlot slot)
        {
            Node* effect = environment()->GetEffectDependency();
            Node* control = environment()->GetControlDependency();
            JSTypeHintLowering::LoweringResult result = type_hint_lowering().ReduceToNumberOperation(value, effect, control,
                slot);
            ApplyEarlyReduction(result);
            return result;
        }

        JSTypeHintLowering::LoweringResult BytecodeGraphBuilder::TryBuildSimplifiedCall(
            const Operator* op, Node* const* args, int arg_count, FeedbackSlot slot)
        {
            Node* effect = environment()->GetEffectDependency();
            Node* control = environment()->GetControlDependency();
            JSTypeHintLowering::LoweringResult result = type_hint_lowering().ReduceCallOperation(op, args, arg_count, effect,
                control, slot);
            ApplyEarlyReduction(result);
            return result;
        }

        JSTypeHintLowering::LoweringResult
        BytecodeGraphBuilder::TryBuildSimplifiedConstruct(const Operator* op,
            Node* const* args,
            int arg_count,
            FeedbackSlot slot)
        {
            Node* effect = environment()->GetEffectDependency();
            Node* control = environment()->GetControlDependency();
            JSTypeHintLowering::LoweringResult result = type_hint_lowering().ReduceConstructOperation(op, args, arg_count, effect,
                control, slot);
            ApplyEarlyReduction(result);
            return result;
        }

        JSTypeHintLowering::LoweringResult
        BytecodeGraphBuilder::TryBuildSimplifiedLoadNamed(const Operator* op,
            Node* receiver,
            FeedbackSlot slot)
        {
            Node* effect = environment()->GetEffectDependency();
            Node* control = environment()->GetControlDependency();
            JSTypeHintLowering::LoweringResult early_reduction = type_hint_lowering().ReduceLoadNamedOperation(op, receiver, effect,
                control, slot);
            ApplyEarlyReduction(early_reduction);
            return early_reduction;
        }

        JSTypeHintLowering::LoweringResult
        BytecodeGraphBuilder::TryBuildSimplifiedLoadKeyed(const Operator* op,
            Node* receiver, Node* key,
            FeedbackSlot slot)
        {
            Node* effect = environment()->GetEffectDependency();
            Node* control = environment()->GetControlDependency();
            JSTypeHintLowering::LoweringResult result = type_hint_lowering().ReduceLoadKeyedOperation(op, receiver, key, effect,
                control, slot);
            ApplyEarlyReduction(result);
            return result;
        }

        JSTypeHintLowering::LoweringResult
        BytecodeGraphBuilder::TryBuildSimplifiedStoreNamed(const Operator* op,
            Node* receiver, Node* value,
            FeedbackSlot slot)
        {
            Node* effect = environment()->GetEffectDependency();
            Node* control = environment()->GetControlDependency();
            JSTypeHintLowering::LoweringResult result = type_hint_lowering().ReduceStoreNamedOperation(op, receiver, value,
                effect, control, slot);
            ApplyEarlyReduction(result);
            return result;
        }

        JSTypeHintLowering::LoweringResult
        BytecodeGraphBuilder::TryBuildSimplifiedStoreKeyed(const Operator* op,
            Node* receiver, Node* key,
            Node* value,
            FeedbackSlot slot)
        {
            Node* effect = environment()->GetEffectDependency();
            Node* control = environment()->GetControlDependency();
            JSTypeHintLowering::LoweringResult result = type_hint_lowering().ReduceStoreKeyedOperation(op, receiver, key, value,
                effect, control, slot);
            ApplyEarlyReduction(result);
            return result;
        }

        void BytecodeGraphBuilder::ApplyEarlyReduction(
            JSTypeHintLowering::LoweringResult reduction)
        {
            if (reduction.IsExit()) {
                MergeControlToLeaveFunction(reduction.control());
            } else if (reduction.IsSideEffectFree()) {
                environment()->UpdateEffectDependency(reduction.effect());
                environment()->UpdateControlDependency(reduction.control());
            } else {
                DCHECK(!reduction.Changed());
                // At the moment, we assume side-effect free reduction. To support
                // side-effects, we would have to invalidate the eager checkpoint,
                // so that deoptimization does not repeat the side effect.
            }
        }

        Node** BytecodeGraphBuilder::EnsureInputBufferSize(int size)
        {
            if (size > input_buffer_size_) {
                size = size + kInputBufferSizeIncrement + input_buffer_size_;
                input_buffer_ = local_zone()->NewArray<Node*>(size);
                input_buffer_size_ = size;
            }
            return input_buffer_;
        }

        void BytecodeGraphBuilder::ExitThenEnterExceptionHandlers(int current_offset)
        {
            HandlerTable table(*bytecode_array());

            // Potentially exit exception handlers.
            while (!exception_handlers_.empty()) {
                int current_end = exception_handlers_.top().end_offset_;
                if (current_offset < current_end)
                    break; // Still covered by range.
                exception_handlers_.pop();
            }

            // Potentially enter exception handlers.
            int num_entries = table.NumberOfRangeEntries();
            while (current_exception_handler_ < num_entries) {
                int next_start = table.GetRangeStart(current_exception_handler_);
                if (current_offset < next_start)
                    break; // Not yet covered by range.
                int next_end = table.GetRangeEnd(current_exception_handler_);
                int next_handler = table.GetRangeHandler(current_exception_handler_);
                int context_register = table.GetRangeData(current_exception_handler_);
                exception_handlers_.push(
                    { next_start, next_end, next_handler, context_register });
                current_exception_handler_++;
            }
        }

        Node* BytecodeGraphBuilder::MakeNode(const Operator* op, int value_input_count,
            Node* const* value_inputs,
            bool incomplete)
        {
            DCHECK_EQ(op->ValueInputCount(), value_input_count);

            bool has_context = OperatorProperties::HasContextInput(op);
            bool has_frame_state = OperatorProperties::HasFrameStateInput(op);
            bool has_control = op->ControlInputCount() == 1;
            bool has_effect = op->EffectInputCount() == 1;

            DCHECK_LT(op->ControlInputCount(), 2);
            DCHECK_LT(op->EffectInputCount(), 2);

            Node* result = nullptr;
            if (!has_context && !has_frame_state && !has_control && !has_effect) {
                result = graph()->NewNode(op, value_input_count, value_inputs, incomplete);
            } else {
                bool inside_handler = !exception_handlers_.empty();
                int input_count_with_deps = value_input_count;
                if (has_context)
                    ++input_count_with_deps;
                if (has_frame_state)
                    ++input_count_with_deps;
                if (has_control)
                    ++input_count_with_deps;
                if (has_effect)
                    ++input_count_with_deps;
                Node** buffer = EnsureInputBufferSize(input_count_with_deps);
                if (value_input_count > 0) {
                    memcpy(buffer, value_inputs, kSystemPointerSize * value_input_count);
                }
                Node** current_input = buffer + value_input_count;
                if (has_context) {
                    *current_input++ = OperatorProperties::NeedsExactContext(op)
                        ? environment()->Context()
                        : jsgraph()->HeapConstant(native_context());
                }
                if (has_frame_state) {
                    // The frame state will be inserted later. Here we misuse the {Dead} node
                    // as a sentinel to be later overwritten with the real frame state by the
                    // calls to {PrepareFrameState} within individual visitor methods.
                    *current_input++ = jsgraph()->Dead();
                }
                if (has_effect) {
                    *current_input++ = environment()->GetEffectDependency();
                }
                if (has_control) {
                    *current_input++ = environment()->GetControlDependency();
                }
                result = graph()->NewNode(op, input_count_with_deps, buffer, incomplete);
                // Update the current control dependency for control-producing nodes.
                if (result->op()->ControlOutputCount() > 0) {
                    environment()->UpdateControlDependency(result);
                }
                // Update the current effect dependency for effect-producing nodes.
                if (result->op()->EffectOutputCount() > 0) {
                    environment()->UpdateEffectDependency(result);
                }
                // Add implicit exception continuation for throwing nodes.
                if (!result->op()->HasProperty(Operator::kNoThrow) && inside_handler) {
                    int handler_offset = exception_handlers_.top().handler_offset_;
                    int context_index = exception_handlers_.top().context_register_;
                    interpreter::Register context_register(context_index);
                    Environment* success_env = environment()->Copy();
                    const Operator* op = common()->IfException();
                    Node* effect = environment()->GetEffectDependency();
                    Node* on_exception = graph()->NewNode(op, effect, result);
                    Node* context = environment()->LookupRegister(context_register);
                    environment()->UpdateControlDependency(on_exception);
                    environment()->UpdateEffectDependency(on_exception);
                    environment()->BindAccumulator(on_exception);
                    environment()->SetContext(context);
                    MergeIntoSuccessorEnvironment(handler_offset);
                    set_environment(success_env);
                }
                // Add implicit success continuation for throwing nodes.
                if (!result->op()->HasProperty(Operator::kNoThrow) && inside_handler) {
                    const Operator* if_success = common()->IfSuccess();
                    Node* on_success = graph()->NewNode(if_success, result);
                    environment()->UpdateControlDependency(on_success);
                }
                // Ensure checkpoints are created after operations with side-effects.
                if (has_effect && !result->op()->HasProperty(Operator::kNoWrite)) {
                    mark_as_needing_eager_checkpoint(true);
                }
            }

            return result;
        }

        Node* BytecodeGraphBuilder::NewPhi(int count, Node* input, Node* control)
        {
            const Operator* phi_op = common()->Phi(MachineRepresentation::kTagged, count);
            Node** buffer = EnsureInputBufferSize(count + 1);
            MemsetPointer(buffer, input, count);
            buffer[count] = control;
            return graph()->NewNode(phi_op, count + 1, buffer, true);
        }

        Node* BytecodeGraphBuilder::NewEffectPhi(int count, Node* input,
            Node* control)
        {
            const Operator* phi_op = common()->EffectPhi(count);
            Node** buffer = EnsureInputBufferSize(count + 1);
            MemsetPointer(buffer, input, count);
            buffer[count] = control;
            return graph()->NewNode(phi_op, count + 1, buffer, true);
        }

        Node* BytecodeGraphBuilder::MergeControl(Node* control, Node* other)
        {
            int inputs = control->op()->ControlInputCount() + 1;
            if (control->opcode() == IrOpcode::kLoop) {
                // Control node for loop exists, add input.
                const Operator* op = common()->Loop(inputs);
                control->AppendInput(graph_zone(), other);
                NodeProperties::ChangeOp(control, op);
            } else if (control->opcode() == IrOpcode::kMerge) {
                // Control node for merge exists, add input.
                const Operator* op = common()->Merge(inputs);
                control->AppendInput(graph_zone(), other);
                NodeProperties::ChangeOp(control, op);
            } else {
                // Control node is a singleton, introduce a merge.
                const Operator* op = common()->Merge(inputs);
                Node* merge_inputs[] = { control, other };
                control = graph()->NewNode(op, arraysize(merge_inputs), merge_inputs, true);
            }
            return control;
        }

        Node* BytecodeGraphBuilder::MergeEffect(Node* value, Node* other,
            Node* control)
        {
            int inputs = control->op()->ControlInputCount();
            if (value->opcode() == IrOpcode::kEffectPhi && NodeProperties::GetControlInput(value) == control) {
                // Phi already exists, add input.
                value->InsertInput(graph_zone(), inputs - 1, other);
                NodeProperties::ChangeOp(value, common()->EffectPhi(inputs));
            } else if (value != other) {
                // Phi does not exist yet, introduce one.
                value = NewEffectPhi(inputs, value, control);
                value->ReplaceInput(inputs - 1, other);
            }
            return value;
        }

        Node* BytecodeGraphBuilder::MergeValue(Node* value, Node* other,
            Node* control)
        {
            int inputs = control->op()->ControlInputCount();
            if (value->opcode() == IrOpcode::kPhi && NodeProperties::GetControlInput(value) == control) {
                // Phi already exists, add input.
                value->InsertInput(graph_zone(), inputs - 1, other);
                NodeProperties::ChangeOp(
                    value, common()->Phi(MachineRepresentation::kTagged, inputs));
            } else if (value != other) {
                // Phi does not exist yet, introduce one.
                value = NewPhi(inputs, value, control);
                value->ReplaceInput(inputs - 1, other);
            }
            return value;
        }

        void BytecodeGraphBuilder::UpdateSourcePosition(SourcePositionTableIterator* it,
            int offset)
        {
            if (it->done())
                return;
            if (it->code_offset() == offset) {
                source_positions_->SetCurrentPosition(SourcePosition(
                    it->source_position().ScriptOffset(), start_position_.InliningId()));
                it->Advance();
            } else {
                DCHECK_GT(it->code_offset(), offset);
            }
        }

    } // namespace compiler
} // namespace internal
} // namespace v8
